Creating captchas in Python

In making this website and in my 4th year honours project I implemented a captcha (which you can see if you try and make a comment). I thought this would be a bit of a nightmare to do, but with Steven’s help and the awesomeness of Python, it was quite easy. The code originally comes from here, but I have made a few edits such as keeping the image in memory.

You will need a .ttf font file to generate the image. You can specify the font size of the text, and the image generated will automatically adjust to fit in the word.

# python imports
import random
import Image
import ImageFont
import ImageDraw
import ImageFilter
from cStringIO import StringIO
from os import path
# local imports
from lowmanio.utils.utils import ROOT_DIR

 
"""
    This code is taken from and is copyright to:
    http://code.activestate.com/recipes/440588/
"""
 
FONT_FILE = path.join(ROOT_DIR, 'lowmanio', 'utils', 'arialbd.ttf')

 
def gen_captcha(text, fnt, fnt_sz, f, fmt='JPEG'):
    """Generate a captcha image"""
    
    # randomly select the foreground color
    fgcolor = random.randint(0,0xffff00)

    # make the background color the opposite of fgcolor
    bgcolor = fgcolor ^ 0xffffff
    
    # create a font object 
    font = ImageFont.truetype(fnt,fnt_sz)
    
    # determine dimensions of the text
    dim = font.getsize(text)
    
    # create a new image slightly larger that the text
    im = Image.new('RGB', (dim[0]+5,dim[1]+5), bgcolor)
    d = ImageDraw.Draw(im)
    x, y = im.size
    r = random.randint
    
    # draw 100 random colored boxes on the background
    for num in range(100):
        d.rectangle((r(0,x),r(0,y),r(0,x),r(0,y)),fill=r(0,0xffffff))

    # add the text to the image
    d.text((3,3), text, font=font, fill=fgcolor)
    im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
    
    # save the image to a file
    im.save(f, format=fmt)
 
def gen_random_word(wordLen=6):
    """Generate a random word of length wordLen. Some characters have been removed
    to avoid ambiguity such as i,l,o,I,L,0, and 1"""
 
    allowedChars = "abcdefghjkmnpqrstuvwzyzABCDEFGHJKMNPQRSTUVWZYZ23456789"
    word = ""

    for i in range(0, wordLen):
        word = word + allowedChars[random.randint(0,0xffffff) % len(allowedChars)]

    return word
 
def generateCaptcha():
    """Generate a captcha image in memory using a randomly generated word and a font 
    file on the system. Returns the word and the image"""
    
    word = gen_random_word()
    buf = StringIO()
    gen_captcha(word.strip(), FONT_FILE, 50, buf)
    s = buf.getvalue()
    buf.close()
    return word, s

When the page with the captcha is loaded, the captcha image URL controller method saves the word in the session, and returns the image to be displayed. This makes sure the images aren’t cached.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s