changeset 87:4775b4195b4b

code pour la generation de captchas
author goldfinger
date Thu, 11 Feb 2010 05:09:46 -0500
parents b3d76ebf2fac
children ab57cd2b252c
files pycaptcha/.DS_Store pycaptcha/BUGS pycaptcha/COPYING pycaptcha/Captcha/.DS_Store pycaptcha/Captcha/Base.py pycaptcha/Captcha/Base.pyc pycaptcha/Captcha/File.py pycaptcha/Captcha/File.pyc pycaptcha/Captcha/Visual/Backgrounds.py pycaptcha/Captcha/Visual/Backgrounds.pyc pycaptcha/Captcha/Visual/Base.py pycaptcha/Captcha/Visual/Base.pyc pycaptcha/Captcha/Visual/Base.py~ pycaptcha/Captcha/Visual/Distortions.py pycaptcha/Captcha/Visual/Distortions.pyc pycaptcha/Captcha/Visual/Distortions.py~ pycaptcha/Captcha/Visual/Pictures.py pycaptcha/Captcha/Visual/Pictures.pyc pycaptcha/Captcha/Visual/Tests.py pycaptcha/Captcha/Visual/Tests.pyc pycaptcha/Captcha/Visual/Tests.py~ pycaptcha/Captcha/Visual/Text.py pycaptcha/Captcha/Visual/Text.pyc pycaptcha/Captcha/Visual/Text.py~ pycaptcha/Captcha/Visual/__init__.py pycaptcha/Captcha/Visual/__init__.pyc pycaptcha/Captcha/Words.py pycaptcha/Captcha/Words.pyc pycaptcha/Captcha/Words.py~ pycaptcha/Captcha/__init__.py pycaptcha/Captcha/__init__.pyc pycaptcha/Captcha/data/.DS_Store pycaptcha/Captcha/data/fonts/.DS_Store pycaptcha/Captcha/data/fonts/others/._atari-small.bdf pycaptcha/Captcha/data/fonts/others/._cursive.bdf pycaptcha/Captcha/data/fonts/others/._radon-wide.bdf pycaptcha/Captcha/data/fonts/others/CIDFnmap pycaptcha/Captcha/data/fonts/others/FAPIfontmap pycaptcha/Captcha/data/fonts/others/Fontmap pycaptcha/Captcha/data/fonts/others/cidfmap pycaptcha/Captcha/data/fonts/vera/COPYRIGHT.TXT pycaptcha/Captcha/data/fonts/vera/README.TXT pycaptcha/Captcha/data/fonts/vera/RELEASENOTES.TXT pycaptcha/Captcha/data/fonts/vera/Vera.ttf pycaptcha/Captcha/data/fonts/vera/VeraBI.ttf pycaptcha/Captcha/data/fonts/vera/VeraBd.ttf pycaptcha/Captcha/data/fonts/vera/VeraIt.ttf pycaptcha/Captcha/data/fonts/vera/VeraMoBI.ttf pycaptcha/Captcha/data/fonts/vera/VeraMoBd.ttf pycaptcha/Captcha/data/fonts/vera/VeraMoIt.ttf pycaptcha/Captcha/data/fonts/vera/VeraMono.ttf pycaptcha/Captcha/data/fonts/vera/VeraSe.ttf pycaptcha/Captcha/data/fonts/vera/VeraSeBd.ttf pycaptcha/Captcha/data/fonts/vera/local.conf pycaptcha/Captcha/data/pictures/.DS_Store pycaptcha/Captcha/data/pictures/abstract/1.jpeg pycaptcha/Captcha/data/pictures/abstract/10.jpeg pycaptcha/Captcha/data/pictures/abstract/11.jpeg pycaptcha/Captcha/data/pictures/abstract/12.jpeg pycaptcha/Captcha/data/pictures/abstract/2.jpeg pycaptcha/Captcha/data/pictures/abstract/3.jpeg pycaptcha/Captcha/data/pictures/abstract/4.jpeg pycaptcha/Captcha/data/pictures/abstract/5.jpeg pycaptcha/Captcha/data/pictures/abstract/6.jpeg pycaptcha/Captcha/data/pictures/abstract/7.jpeg pycaptcha/Captcha/data/pictures/abstract/8.jpeg pycaptcha/Captcha/data/pictures/abstract/9.jpeg pycaptcha/Captcha/data/pictures/abstract/README pycaptcha/Captcha/data/pictures/nature/Craig_Barrington_ocotillo_and_mountains.jpeg pycaptcha/Captcha/data/pictures/nature/Kerry_Carloy_Chisos_Sunset.jpeg pycaptcha/Captcha/data/pictures/nature/Paul_Dowty_Mt_Bross.jpeg pycaptcha/Captcha/data/pictures/nature/README pycaptcha/Captcha/data/words/README pycaptcha/Captcha/data/words/basic-english pycaptcha/Captcha/data/words/characters pycaptcha/Facade.py pycaptcha/Facade.pyc pycaptcha/Facade.py~ pycaptcha/README pycaptcha/http_example.py pycaptcha/modpython_example.py pycaptcha/output.png pycaptcha/setup.py pycaptcha/setup/__init__.py pycaptcha/setup/my_install_data.py pycaptcha/simple_example.py pycaptcha/simple_example.py~ pycaptcha/test.png pycaptcha/transformations.py pycaptcha/transformations.pyc pycaptcha/transformations.py~
diffstat 91 files changed, 3402 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
Binary file pycaptcha/.DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/BUGS	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,7 @@
+Known bugs:
+
+- PersistentFactory() is almost certainly horrible at concurrent access
+- Tests are never invalidated with PersistentStorage(), as they aren't written back to the database
+- All files in Captcha/data are installed, including silly things like .svn directories and *~
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/COPYING	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,19 @@
+Copyright (c) 2004 Micah Dowty
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE. 
Binary file pycaptcha/Captcha/.DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Base.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,127 @@
+""" Captcha.Base
+
+Base class for all types of CAPTCHA tests. All tests have one or
+more solution, determined when the test is generated. Solutions
+can be any python object,
+
+All tests can be solved by presenting at least some preset number
+of correct solutions. Some tests may only have one solution and require
+one solution, but other tests may require N correct solutions of M
+possible solutions.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, string, time, shelve
+
+__all__ = ["BaseCaptcha", "Factory", "PersistentFactory"]
+
+
+def randomIdentifier(alphabet = string.ascii_letters + string.digits,
+                     length = 24):
+    return "".join([random.choice(alphabet) for i in xrange(length)])
+
+
+class BaseCaptcha(object):
+    """Base class for all CAPTCHA tests"""
+    # Subclasses can override these to set the solution criteria
+    minCorrectSolutions = 1
+    maxIncorrectSolutions = 0
+
+    def __init__(self):
+        self.solutions = []
+        self.valid = True
+
+        # Each test has a unique identifier, used to refer to that test
+        # later, and a creation time so it can expire later.
+        self.id = randomIdentifier()
+        self.creationTime = time.time()
+
+    def addSolution(self, solution):
+        self.solutions.append(solution)
+
+    def testSolutions(self, solutions):
+        """Test whether the given solutions are sufficient for this CAPTCHA.
+           A given CAPTCHA can only be tested once, after that it is invalid
+           and always returns False. This makes random guessing much less effective.
+           """
+        if not self.valid:
+            return False
+        self.valid = False
+
+        numCorrect = 0
+        numIncorrect = 0
+
+        for solution in solutions:
+            if solution in self.solutions:
+                numCorrect += 1
+            else:
+                numIncorrect += 1
+
+        return numCorrect >= self.minCorrectSolutions and \
+               numIncorrect <= self.maxIncorrectSolutions
+
+
+class Factory(object):
+    """Creates BaseCaptcha instances on demand, and tests solutions.
+       CAPTCHAs expire after a given amount of time, given in seconds.
+       The default is 15 minutes.
+       """
+    def __init__(self, lifetime=60*15):
+        self.lifetime = lifetime
+        self.storedInstances = {}
+
+    def new(self, cls, *args, **kwargs):
+        """Create a new instance of our assigned BaseCaptcha subclass, passing
+           it any extra arguments we're given. This stores the result for
+           later testing.
+           """
+        self.clean()
+        inst = cls(*args, **kwargs)
+        self.storedInstances[inst.id] = inst
+        return inst
+
+    def get(self, id):
+        """Retrieve the CAPTCHA with the given ID. If it's expired already,
+           this will return None. A typical web application will need to
+           new() a CAPTCHA when generating an html page, then get() it later
+           when its images or sounds must be rendered.
+           """
+        return self.storedInstances.get(id)
+
+    def clean(self):
+        """Removed expired tests"""
+        expiredIds = []
+        now = time.time()
+        for inst in self.storedInstances.itervalues():
+            if inst.creationTime + self.lifetime < now:
+                expiredIds.append(inst.id)
+        for id in expiredIds:
+            del self.storedInstances[id]
+
+    def test(self, id, solutions):
+        """Test the given list of solutions against the BaseCaptcha instance
+           created earlier with the given id. Returns True if the test passed,
+           False on failure. In either case, the test is invalidated. Returns
+           False in the case of an invalid id.
+           """
+        self.clean()
+        inst = self.storedInstances.get(id)
+        if not inst:
+            return False
+        result = inst.testSolutions(solutions)
+        return result
+
+
+class PersistentFactory(Factory):
+    """A simple persistent factory, for use in CGI or multi-process environments
+       where the state must remain across python interpreter sessions.
+       This implementation uses the 'shelve' module.
+       """
+    def __init__(self, filename, lifetime=60*15):
+        Factory.__init__(self, lifetime)
+	self.storedInstances = shelve.open(filename)
+
+### The End ###
Binary file pycaptcha/Captcha/Base.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/File.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,53 @@
+""" Captcha.File
+
+Utilities for finding and picking random files from our 'data' directory
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import os, random
+
+# Determine the data directory. This can be overridden after import-time if needed.
+dataDir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data")
+
+
+class RandomFileFactory(object):
+    """Given a list of files and/or directories, this picks a random file.
+       Directories are searched for files matching any of a list of extensions.
+       Files are relative to our data directory plus a subclass-specified base path.
+       """
+    extensions = []
+    basePath = "."
+
+    def __init__(self, *fileList):
+        self.fileList = fileList
+        self._fullPaths = None
+
+    def _checkExtension(self, name):
+        """Check the file against our given list of extensions"""
+        for ext in self.extensions:
+            if name.endswith(ext):
+                return True
+        return False
+
+    def _findFullPaths(self):
+        """From our given file list, find a list of full paths to files"""
+        paths = []
+        for name in self.fileList:
+            path = os.path.join(dataDir, self.basePath, name)
+            if os.path.isdir(path):
+                for content in os.listdir(path):
+                    if self._checkExtension(content):
+                        paths.append(os.path.join(path, content))
+            else:
+                paths.append(path)
+        return paths
+
+    def pick(self):
+        if self._fullPaths is None:
+            self._fullPaths = self._findFullPaths()
+        return random.choice(self._fullPaths)
+
+### The End ###
Binary file pycaptcha/Captcha/File.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Backgrounds.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,95 @@
+""" Captcha.Visual.Backgrounds
+
+Background layers for visual CAPTCHAs
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha.Visual import Layer, Pictures
+import random, os
+import ImageDraw, Image
+
+
+class SolidColor(Layer):
+    """A solid color background. Very weak on its own, but good
+       to combine with other backgrounds.
+       """
+    def __init__(self, color="white"):
+        self.color = color
+
+    def render(self, image):
+        image.paste(self.color)
+
+
+class Grid(Layer):
+    """A grid of lines, with a given foreground color.
+       The size is given in pixels. The background is transparent,
+       so another layer (like SolidColor) should be put behind it.
+       """
+    def __init__(self, size=16, foreground="black"):
+        self.size = size
+        self.foreground = foreground
+        self.offset = (random.uniform(0, self.size),
+                       random.uniform(0, self.size))
+
+    def render(self, image):
+        draw = ImageDraw.Draw(image)
+
+        for i in xrange(image.size[0] / self.size + 1):
+            draw.line( (i*self.size+self.offset[0], 0,
+                        i*self.size+self.offset[0], image.size[1]), fill=self.foreground)
+
+        for i in xrange(image.size[0] / self.size + 1):
+            draw.line( (0, i*self.size+self.offset[1],
+                        image.size[0], i*self.size+self.offset[1]), fill=self.foreground)
+
+
+class TiledImage(Layer):
+    """Pick a random image and a random offset, and tile the rendered image with it"""
+    def __init__(self, imageFactory=Pictures.abstract):
+        self.tileName = imageFactory.pick()
+        self.offset = (random.uniform(0, 1),
+                       random.uniform(0, 1))
+
+    def render(self, image):
+        tile = Image.open(self.tileName)
+        for j in xrange(-1, int(image.size[1] / tile.size[1]) + 1):
+            for i in xrange(-1, int(image.size[0] / tile.size[0]) + 1):
+                dest = (int((self.offset[0] + i) * tile.size[0]),
+                        int((self.offset[1] + j) * tile.size[1]))
+                image.paste(tile, dest)
+
+
+class CroppedImage(Layer):
+    """Pick a random image, cropped randomly. Source images should be larger than the CAPTCHA."""
+    def __init__(self, imageFactory=Pictures.nature):
+        self.imageName = imageFactory.pick()
+        self.align = (random.uniform(0,1),
+                      random.uniform(0,1))
+
+    def render(self, image):
+        i = Image.open(self.imageName)
+        image.paste(i, (int(self.align[0] * (image.size[0] - i.size[0])),
+                        int(self.align[1] * (image.size[1] - i.size[1]))))
+
+
+class RandomDots(Layer):
+    """Draw random colored dots"""
+    def __init__(self, colors=("white", "black"), dotSize=4, numDots=400):
+        self.colors = colors
+        self.dotSize = dotSize
+        self.numDots = numDots
+	self.seed = random.random()
+
+    def render(self, image):
+        r = random.Random(self.seed)
+        for i in xrange(self.numDots):
+            bx = int(r.uniform(0, image.size[0]-self.dotSize))
+            by = int(r.uniform(0, image.size[1]-self.dotSize))
+            image.paste(r.choice(self.colors), (bx, by,
+                                                bx+self.dotSize-1,
+                                                by+self.dotSize-1))
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Backgrounds.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Base.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,70 @@
+""" Captcha.Visual.BAse
+
+Base classes for visual CAPTCHAs. We use the Python Imaging Library
+to manipulate these images.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import Captcha
+import Image
+
+__all__ = ['ImageCaptcha', 'Layer']
+
+
+class ImageCaptcha(Captcha.BaseCaptcha):
+    """Base class for image-based CAPTCHA tests.
+       The render() function generates the CAPTCHA image at the given size by
+       combining Layer instances from self.layers, which should be created by
+       the subclass-defined getLayers().
+       """
+    defaultSize = (32,32)
+    # anciennement a defaultSize(256,96)
+    def __init__(self, *args, **kwargs):
+        Captcha.BaseCaptcha.__init__(self)
+        self._layers = self.getLayers(*args, **kwargs)
+
+    def getImage(self):
+        """Get a PIL image representing this CAPTCHA test, creating it if necessary"""
+        if not self._image:
+            self._image = self.render()
+        return self._image
+
+    def getLayers(self):
+        """Subclasses must override this to return a list of Layer instances to render.
+           Lists within the list of layers are recursively rendered.
+           """
+        return []
+
+    def render(self, size=None):
+        """Render this CAPTCHA, returning a PIL image"""
+        if size is None:
+            size = self.defaultSize
+        img = Image.new("L", size)
+       # img = Image.new("RGB", size)
+        return self._renderList(self._layers, Image.new("L", size))
+
+    def _renderList(self, l, img):
+        for i in l:
+            if type(i) == tuple or type(i) == list:
+                img = self._renderList(i, img)
+            else:
+                img = i.render(img) or img
+        return img
+
+
+class Layer(object):
+    """A renderable object representing part of a CAPTCHA.
+       The render() function should return approximately the same result, regardless
+       of the image size. This means any randomization must occur in the constructor.
+
+       If the render() function returns something non-None, it is taken as an image to
+       replace the current image with. This can be used to implement transformations
+       that result in a separate image without having to copy the results back to the first.
+       """
+    def render(self, img):
+        pass
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Base.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Base.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,69 @@
+""" Captcha.Visual.BAse
+
+Base classes for visual CAPTCHAs. We use the Python Imaging Library
+to manipulate these images.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import Captcha
+import Image
+
+__all__ = ['ImageCaptcha', 'Layer']
+
+
+class ImageCaptcha(Captcha.BaseCaptcha):
+    """Base class for image-based CAPTCHA tests.
+       The render() function generates the CAPTCHA image at the given size by
+       combining Layer instances from self.layers, which should be created by
+       the subclass-defined getLayers().
+       """
+    defaultSize = (256,96)
+
+    def __init__(self, *args, **kwargs):
+        Captcha.BaseCaptcha.__init__(self)
+        self._layers = self.getLayers(*args, **kwargs)
+
+    def getImage(self):
+        """Get a PIL image representing this CAPTCHA test, creating it if necessary"""
+        if not self._image:
+            self._image = self.render()
+        return self._image
+
+    def getLayers(self):
+        """Subclasses must override this to return a list of Layer instances to render.
+           Lists within the list of layers are recursively rendered.
+           """
+        return []
+
+    def render(self, size=None):
+        """Render this CAPTCHA, returning a PIL image"""
+        if size is None:
+            size = self.defaultSize
+        img = Image.new("RGB", size)
+        return self._renderList(self._layers, Image.new("RGB", size))
+
+    def _renderList(self, l, img):
+        for i in l:
+            if type(i) == tuple or type(i) == list:
+                img = self._renderList(i, img)
+            else:
+                img = i.render(img) or img
+        return img
+
+
+class Layer(object):
+    """A renderable object representing part of a CAPTCHA.
+       The render() function should return approximately the same result, regardless
+       of the image size. This means any randomization must occur in the constructor.
+
+       If the render() function returns something non-None, it is taken as an image to
+       replace the current image with. This can be used to implement transformations
+       that result in a separate image without having to copy the results back to the first.
+       """
+    def render(self, img):
+        pass
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Distortions.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,117 @@
+""" Captcha.Visual.Distortions
+
+Distortion layers for visual CAPTCHAs
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha.Visual import Layer
+import ImageDraw, Image
+import random, math
+
+
+class WigglyBlocks(Layer):
+    """Randomly select and shift blocks of the image"""
+    def __init__(self, blockSize=3, sigma=0.01, iterations=300):
+        self.blockSize = blockSize
+        self.sigma = sigma
+        self.iterations = iterations
+        self.seed = random.random()
+
+    def render(self, image):
+        r = random.Random(self.seed)
+        for i in xrange(self.iterations):
+            # Select a block
+            bx = int(r.uniform(0, image.size[0]-self.blockSize))
+            by = int(r.uniform(0, image.size[1]-self.blockSize))
+            block = image.crop((bx, by, bx+self.blockSize-1, by+self.blockSize-1))
+
+            # Figure out how much to move it.
+            # The call to floor() is important so we always round toward
+            # 0 rather than to -inf. Just int() would bias the block motion.
+            mx = int(math.floor(r.normalvariate(0, self.sigma)))
+            my = int(math.floor(r.normalvariate(0, self.sigma)))
+
+            # Now actually move the block
+            image.paste(block, (bx+mx, by+my))
+
+
+class WarpBase(Layer):
+    """Abstract base class for image warping. Subclasses define a
+       function that maps points in the output image to points in the input image.
+       This warping engine runs a grid of points through this transform and uses
+       PIL's mesh transform to warp the image.
+       """
+    filtering = Image.BILINEAR
+    resolution = 10
+
+    def getTransform(self, image):
+        """Return a transformation function, subclasses should override this"""
+        return lambda x, y: (x, y)
+
+    def render(self, image):
+        r = self.resolution
+        xPoints = image.size[0] / r + 2
+        yPoints = image.size[1] / r + 2
+        f = self.getTransform(image)
+
+        # Create a list of arrays with transformed points
+        xRows = []
+        yRows = []
+        for j in xrange(yPoints):
+            xRow = []
+            yRow = []
+            for i in xrange(xPoints):
+                x, y = f(i*r, j*r)
+
+                # Clamp the edges so we don't get black undefined areas
+                x = max(0, min(image.size[0]-1, x))
+                y = max(0, min(image.size[1]-1, y))
+
+                xRow.append(x)
+                yRow.append(y)
+            xRows.append(xRow)
+            yRows.append(yRow)
+
+        # Create the mesh list, with a transformation for
+        # each square between points on the grid
+        mesh = []
+        for j in xrange(yPoints-1):
+            for i in xrange(xPoints-1):
+                mesh.append((
+                    # Destination rectangle
+                    (i*r, j*r,
+                     (i+1)*r, (j+1)*r),
+                    # Source quadrilateral
+                    (xRows[j  ][i  ], yRows[j  ][i  ],
+                     xRows[j+1][i  ], yRows[j+1][i  ],
+                     xRows[j+1][i+1], yRows[j+1][i+1],
+                     xRows[j  ][i+1], yRows[j  ][i+1]),
+                    ))
+
+        return image.transform(image.size, Image.MESH, mesh, self.filtering)
+
+
+class SineWarp(WarpBase):
+    """Warp the image using a random composition of sine waves"""
+
+    def __init__(self,
+                 amplitudeRange = (3, 6.5),
+                 periodRange    = (0.04, 0.1),
+                 ):
+        self.amplitude = random.uniform(*amplitudeRange)
+        self.period = random.uniform(*periodRange)
+        self.offset = (random.uniform(0, math.pi * 2 / self.period),
+                       random.uniform(0, math.pi * 2 / self.period))
+
+    def getTransform(self, image):
+        return (lambda x, y,
+                a = self.amplitude,
+                p = self.period,
+                o = self.offset:
+                (math.sin( (y+o[0])*p )*a + x,
+                 math.sin( (x+o[1])*p )*a + y))
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Distortions.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Distortions.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,117 @@
+""" Captcha.Visual.Distortions
+
+Distortion layers for visual CAPTCHAs
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha.Visual import Layer
+import ImageDraw, Image
+import random, math
+
+
+class WigglyBlocks(Layer):
+    """Randomly select and shift blocks of the image"""
+    def __init__(self, blockSize=3, sigma=0.01, iterations=300):
+        self.blockSize = blockSize
+        self.sigma = sigma
+        self.iterations = iterations
+        self.seed = random.random()
+
+    def render(self, image):
+        r = random.Random(self.seed)
+        for i in xrange(self.iterations):
+            # Select a block
+            bx = int(r.uniform(0, image.size[0]-self.blockSize))
+            by = int(r.uniform(0, image.size[1]-self.blockSize))
+            block = image.crop((bx, by, bx+self.blockSize-1, by+self.blockSize-1))
+
+            # Figure out how much to move it.
+            # The call to floor() is important so we always round toward
+            # 0 rather than to -inf. Just int() would bias the block motion.
+            mx = int(math.floor(r.normalvariate(0, self.sigma)))
+            my = int(math.floor(r.normalvariate(0, self.sigma)))
+
+            # Now actually move the block
+            image.paste(block, (bx+mx, by+my))
+
+
+class WarpBase(Layer):
+    """Abstract base class for image warping. Subclasses define a
+       function that maps points in the output image to points in the input image.
+       This warping engine runs a grid of points through this transform and uses
+       PIL's mesh transform to warp the image.
+       """
+    filtering = Image.BILINEAR
+    resolution = 10
+
+    def getTransform(self, image):
+        """Return a transformation function, subclasses should override this"""
+        return lambda x, y: (x, y)
+
+    def render(self, image):
+        r = self.resolution
+        xPoints = image.size[0] / r + 2
+        yPoints = image.size[1] / r + 2
+        f = self.getTransform(image)
+
+        # Create a list of arrays with transformed points
+        xRows = []
+        yRows = []
+        for j in xrange(yPoints):
+            xRow = []
+            yRow = []
+            for i in xrange(xPoints):
+                x, y = f(i*r, j*r)
+
+                # Clamp the edges so we don't get black undefined areas
+                x = max(0, min(image.size[0]-1, x))
+                y = max(0, min(image.size[1]-1, y))
+
+                xRow.append(x)
+                yRow.append(y)
+            xRows.append(xRow)
+            yRows.append(yRow)
+
+        # Create the mesh list, with a transformation for
+        # each square between points on the grid
+        mesh = []
+        for j in xrange(yPoints-1):
+            for i in xrange(xPoints-1):
+                mesh.append((
+                    # Destination rectangle
+                    (i*r, j*r,
+                     (i+1)*r, (j+1)*r),
+                    # Source quadrilateral
+                    (xRows[j  ][i  ], yRows[j  ][i  ],
+                     xRows[j+1][i  ], yRows[j+1][i  ],
+                     xRows[j+1][i+1], yRows[j+1][i+1],
+                     xRows[j  ][i+1], yRows[j  ][i+1]),
+                    ))
+
+        return image.transform(image.size, Image.MESH, mesh, self.filtering)
+
+
+class SineWarp(WarpBase):
+    """Warp the image using a random composition of sine waves"""
+
+    def __init__(self,
+                 amplitudeRange = (3, 6.5),
+                 periodRange    = (0.04, 0.1),
+                 ):
+        self.amplitude = random.uniform(*amplitudeRange)
+        self.period = random.uniform(*periodRange)
+        self.offset = (random.uniform(0, math.pi * 2 / self.period),
+                       random.uniform(0, math.pi * 2 / self.period))
+
+    def getTransform(self, image):
+        return (lambda x, y,
+                a = self.amplitude,
+                p = self.period,
+                o = self.offset:
+                (math.sin( (y+o[0])*p )*a + x,
+                 math.sin( (x+o[1])*p )*a + y))
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Pictures.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,23 @@
+""" Captcha.Visual.Pictures
+
+Random collections of images
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha import File
+import Image
+
+
+class ImageFactory(File.RandomFileFactory):
+    """A factory that generates random images from a list"""
+    extensions = [".png", ".jpeg"]
+    basePath = "pictures"
+
+
+abstract = ImageFactory("abstract")
+nature = ImageFactory("nature")
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Pictures.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Tests.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,65 @@
+""" Captcha.Visual.Tests
+
+Visual CAPTCHA tests
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha.Visual import Text, Backgrounds, Distortions, ImageCaptcha
+from Captcha import Words
+import random
+
+__all__ = ["PseudoGimpy", "AngryGimpy", "AntiSpam"]
+
+
+class PseudoGimpy(ImageCaptcha):
+    """A relatively easy CAPTCHA that's somewhat easy on the eyes"""
+    def getLayers(self):
+        word = Words.defaultWordList.pick()
+        self.addSolution(word)
+        return [
+           # random.choice([
+           #     Backgrounds.CroppedImage(),
+           #     Backgrounds.TiledImage(),
+           # ]),
+            Text.TextLayer(word, borderSize=1),
+            Distortions.SineWarp(),
+            ]
+
+
+class AngryGimpy(ImageCaptcha):
+    """A harder but less visually pleasing CAPTCHA"""
+    def getLayers(self):
+        word = Words.defaultWordList.pick()
+        self.addSolution(word)
+        return [
+           # suppression du background 
+           # Backgrounds.TiledImage(),
+           # Backgrounds.RandomDots(),
+            Text.TextLayer(word, borderSize=1),
+	   # Distortions.SineWarp(periodRange    = (0.04, 0.07))
+            Distortions.WigglyBlocks(),
+              ]
+
+
+class AntiSpam(ImageCaptcha):
+    """A fixed-solution CAPTCHA that can be used to hide email addresses or URLs from bots"""
+    fontFactory = Text.FontFactory(20, "vera/VeraBd.ttf")
+    defaultSize = (512,50)
+
+    def getLayers(self, solution="murray@example.com"):
+        self.addSolution(solution)
+
+        textLayer = Text.TextLayer(solution,
+                                   borderSize = 2,
+                                   fontFactory = self.fontFactory)
+
+        return [
+            Backgrounds.CroppedImage(),
+            textLayer,
+            Distortions.SineWarp(amplitudeRange = (3, 5)),
+            ]
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Tests.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Tests.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,65 @@
+""" Captcha.Visual.Tests
+
+Visual CAPTCHA tests
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+from Captcha.Visual import Text, Backgrounds, Distortions, ImageCaptcha
+from Captcha import Words
+import random
+
+__all__ = ["PseudoGimpy", "AngryGimpy", "AntiSpam"]
+
+
+class PseudoGimpy(ImageCaptcha):
+    """A relatively easy CAPTCHA that's somewhat easy on the eyes"""
+    def getLayers(self):
+        word = Words.defaultWordList.pick()
+        self.addSolution(word)
+        return [
+           # random.choice([
+           #     Backgrounds.CroppedImage(),
+           #     Backgrounds.TiledImage(),
+           # ]),
+            Text.TextLayer(word, borderSize=1),
+            Distortions.SineWarp(),
+            ]
+
+
+class AngryGimpy(ImageCaptcha):
+    """A harder but less visually pleasing CAPTCHA"""
+    def getLayers(self):
+        word = Words.defaultWordList.pick()
+        self.addSolution(word)
+        return [
+           # suppression du background 
+           # Backgrounds.TiledImage(),
+           # Backgrounds.RandomDots(),
+            Text.TextLayer(word, borderSize=1),
+	    Distortions.SineWarp(periodRange    = (0.04, 0.07))
+           # Distortions.WigglyBlocks(),
+              ]
+
+
+class AntiSpam(ImageCaptcha):
+    """A fixed-solution CAPTCHA that can be used to hide email addresses or URLs from bots"""
+    fontFactory = Text.FontFactory(20, "vera/VeraBd.ttf")
+    defaultSize = (512,50)
+
+    def getLayers(self, solution="murray@example.com"):
+        self.addSolution(solution)
+
+        textLayer = Text.TextLayer(solution,
+                                   borderSize = 2,
+                                   fontFactory = self.fontFactory)
+
+        return [
+            Backgrounds.CroppedImage(),
+            textLayer,
+            Distortions.SineWarp(amplitudeRange = (3, 5)),
+            ]
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Text.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,101 @@
+""" Captcha.Visual.Text
+
+Text generation for visual CAPTCHAs.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, os
+from Captcha import Visual, File
+import ImageFont, ImageDraw
+
+
+class FontFactory(File.RandomFileFactory):
+    """Picks random fonts and/or sizes from a given list.
+       'sizes' can be a single size or a (min,max) tuple.
+       If any of the given files are directories, all *.ttf found
+       in that directory will be added.
+       """
+    extensions = [".ttf"]
+    basePath = "fonts"
+
+# arguments variables a modifier pour mettre le chemin vers les fontes.
+    def __init__(self, sizes, *fileNames):
+        File.RandomFileFactory.__init__(self, *fileNames)
+
+        if type(sizes) is tuple:			
+            self.minSize = sizes[0]
+            self.maxSize = sizes[1]
+        else:
+            self.minSize = sizes
+            self.maxSize = sizes
+
+    def pick(self):
+        """Returns a (fileName, size) tuple that can be passed to ImageFont.truetype()"""
+        fileName = File.RandomFileFactory.pick(self)
+        size = int(random.uniform(self.minSize, self.maxSize) + 0.5)
+        return (fileName, size)
+
+# Predefined font factories
+defaultFontFactory = FontFactory(25, "vera", "others")
+#defaultFontFactory = FontFactory((30, 40), "vera")
+
+class TextLayer(Visual.Layer):
+    """Represents a piece of text rendered within the image.
+       Alignment is given such that (0,0) places the text in the
+       top-left corner and (1,1) places it in the bottom-left.
+
+       The font and alignment are optional, if not specified one is
+       chosen randomly. If no font factory is specified, the default is used.
+       """
+    def __init__(self, text,
+                 alignment   = None,
+                 font        = None,
+                 fontFactory = None,
+                 textColor   = "white",
+                 borderSize  = 0,
+                 borderColor = None,
+                 ):
+        if fontFactory is None:
+            global defaultFontFactory
+            fontFactory = defaultFontFactory
+
+        if font is None:
+            font = fontFactory.pick()
+
+        if alignment is None:
+            alignment = (random.uniform(0,1),
+                         random.uniform(0,1))
+
+        self.text        = text
+        self.alignment   = alignment
+        self.font        = font
+        self.textColor   = textColor
+        self.borderSize  = borderSize
+        self.borderColor = borderColor
+
+    def render(self, img):
+        font = ImageFont.truetype(*self.font)
+    	textSize = font.getsize(self.text)
+        draw = ImageDraw.Draw(img)
+
+        # Find the text's origin given our alignment and current image size
+        x = int((img.size[0] - textSize[0] - self.borderSize*2) * self.alignment[0] + 0.5)
+        y = int((img.size[1] - textSize[1] - self.borderSize*2) * self.alignment[1] + 0.5)
+
+        # Draw the border if we need one. This is slow and ugly, but there doesn't
+        # seem to be a better way with PIL.
+        if self.borderSize > 0:
+            for bx in (-1,0,1):
+                for by in (-1,0,1):
+                    if bx and by:
+                        draw.text((x + bx * self.borderSize,
+                                   y + by * self.borderSize),
+                                  self.text, font=font, fill=self.borderColor)
+
+        # And the text itself...
+        draw.text((x,y), self.text, font=font, fill=self.textColor)
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/Text.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/Text.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,101 @@
+""" Captcha.Visual.Text
+
+Text generation for visual CAPTCHAs.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, os
+from Captcha import Visual, File
+import ImageFont, ImageDraw
+
+
+class FontFactory(File.RandomFileFactory):
+    """Picks random fonts and/or sizes from a given list.
+       'sizes' can be a single size or a (min,max) tuple.
+       If any of the given files are directories, all *.ttf found
+       in that directory will be added.
+       """
+    extensions = [".ttf", ".bdf"]
+    basePath = "fonts"
+
+# arguments variables a modifier pour mettre le chemin vers les fontes.
+    def __init__(self, sizes, *fileNames):
+        File.RandomFileFactory.__init__(self, *fileNames)
+
+        if type(sizes) is tuple:
+            self.minSize = sizes[0]
+            self.maxSize = sizes[1]
+        else:
+            self.minSize = sizes
+            self.maxSize = sizes
+
+    def pick(self):
+        """Returns a (fileName, size) tuple that can be passed to ImageFont.truetype()"""
+        fileName = File.RandomFileFactory.pick(self)
+        size = int(random.uniform(self.minSize, self.maxSize) + 0.5)
+        return (fileName, size)
+
+# Predefined font factories
+defaultFontFactory = FontFactory(25, "vera", "mlm", "others")
+#defaultFontFactory = FontFactory((30, 40), "vera")
+
+class TextLayer(Visual.Layer):
+    """Represents a piece of text rendered within the image.
+       Alignment is given such that (0,0) places the text in the
+       top-left corner and (1,1) places it in the bottom-left.
+
+       The font and alignment are optional, if not specified one is
+       chosen randomly. If no font factory is specified, the default is used.
+       """
+    def __init__(self, text,
+                 alignment   = None,
+                 font        = None,
+                 fontFactory = None,
+                 textColor   = "white",
+                 borderSize  = 0,
+                 borderColor = None,
+                 ):
+        if fontFactory is None:
+            global defaultFontFactory
+            fontFactory = defaultFontFactory
+
+        if font is None:
+            font = fontFactory.pick()
+
+        if alignment is None:
+            alignment = (random.uniform(0,1),
+                         random.uniform(0,1))
+
+        self.text        = text
+        self.alignment   = alignment
+        self.font        = font
+        self.textColor   = textColor
+        self.borderSize  = borderSize
+        self.borderColor = borderColor
+
+    def render(self, img):
+        font = ImageFont.truetype(*self.font)
+    	textSize = font.getsize(self.text)
+        draw = ImageDraw.Draw(img)
+
+        # Find the text's origin given our alignment and current image size
+        x = int((img.size[0] - textSize[0] - self.borderSize*2) * self.alignment[0] + 0.5)
+        y = int((img.size[1] - textSize[1] - self.borderSize*2) * self.alignment[1] + 0.5)
+
+        # Draw the border if we need one. This is slow and ugly, but there doesn't
+        # seem to be a better way with PIL.
+        if self.borderSize > 0:
+            for bx in (-1,0,1):
+                for by in (-1,0,1):
+                    if bx and by:
+                        draw.text((x + bx * self.borderSize,
+                                   y + by * self.borderSize),
+                                  self.text, font=font, fill=self.borderColor)
+
+        # And the text itself...
+        draw.text((x,y), self.text, font=font, fill=self.textColor)
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Visual/__init__.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,14 @@
+""" Captcha.Visual
+
+This package contains functionality specific to visual CAPTCHA tests.
+
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+# Convenience imports
+from Base import *
+
+### The End ###
Binary file pycaptcha/Captcha/Visual/__init__.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Words.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,58 @@
+""" Captcha.Words
+
+Utilities for managing word lists and finding random words
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, os
+import File
+
+
+class WordList(object):
+    """A class representing a word list read from disk lazily.
+       Blank lines and comment lines starting with '#' are ignored.
+       Any number of words per line may be used. The list can
+       optionally ingore words not within a given length range.
+       """
+    def __init__(self, fileName, minLength=None, maxLength=None):
+        self.words = None
+        self.fileName = fileName
+        self.minLength = minLength
+        self.maxLength = maxLength
+
+    def read(self):
+        """Read words from disk"""
+        f = open(os.path.join(File.dataDir, "words", self.fileName))
+
+        self.words = []
+        for line in f.xreadlines():
+            line = line.strip()
+            if not line:
+                continue
+            if line[0] == '#':
+                continue
+            for word in line.split():
+                if self.minLength is not None and len(word) < self.minLength:
+                    continue
+                if self.maxLength is not None and len(word) > self.maxLength:
+                    continue
+                self.words.append(word)
+
+    def pick(self):
+        """Pick a random word from the list, reading it in if necessary"""
+        if self.words is None:
+            self.read()
+        return random.choice(self.words)
+
+
+# Define several shared word lists that are read from disk on demand
+basic_english            = WordList("basic-english")
+basic_english_restricted = WordList("basic-english", minLength=5, maxLength=8)
+characters = WordList("characters")
+defaultWordList = characters
+
+
+### The End ###
Binary file pycaptcha/Captcha/Words.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/Words.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,57 @@
+""" Captcha.Words
+
+Utilities for managing word lists and finding random words
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, os
+import File
+
+
+class WordList(object):
+    """A class representing a word list read from disk lazily.
+       Blank lines and comment lines starting with '#' are ignored.
+       Any number of words per line may be used. The list can
+       optionally ingore words not within a given length range.
+       """
+    def __init__(self, fileName, minLength=None, maxLength=None):
+        self.words = None
+        self.fileName = fileName
+        self.minLength = minLength
+        self.maxLength = maxLength
+
+    def read(self):
+        """Read words from disk"""
+        f = open(os.path.join(File.dataDir, "words", self.fileName))
+
+        self.words = []
+        for line in f.xreadlines():
+            line = line.strip()
+            if not line:
+                continue
+            if line[0] == '#':
+                continue
+            for word in line.split():
+                if self.minLength is not None and len(word) < self.minLength:
+                    continue
+                if self.maxLength is not None and len(word) > self.maxLength:
+                    continue
+                self.words.append(word)
+
+    def pick(self):
+        """Pick a random word from the list, reading it in if necessary"""
+        if self.words is None:
+            self.read()
+        return random.choice(self.words)
+
+
+# Define several shared word lists that are read from disk on demand
+basic_english            = WordList("basic-english")
+basic_english_restricted = WordList("basic-english", minLength=5, maxLength=8)
+
+defaultWordList = basic_english_restricted
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/__init__.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,41 @@
+""" Captcha
+
+This is the PyCAPTCHA package, a collection of Python modules
+implementing CAPTCHAs: automated tests that humans should pass,
+but current computer programs can't. These tests are often
+used for security.
+
+See  http://www.captcha.net for more information and examples.
+
+This project was started because the CIA project, written in
+Python, needed a CAPTCHA to automate its user creation process
+safely. All existing implementations the author could find were
+written in Java or for the .NET framework, so a simple Python
+alternative was needed.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+__version__ = "0.3-pre"
+
+
+# Check the python version here before we proceed further
+requiredPythonVersion = (2,2,1)
+def checkVersion():
+    import sys, string
+    if sys.version_info < requiredPythonVersion:
+        raise Exception("%s requires at least Python %s, found %s instead." % (
+            name,
+            string.join(map(str, requiredPythonVersion), "."),
+            string.join(map(str, sys.version_info), ".")))
+checkVersion()
+
+
+# Convenience imports
+from Base import *
+import File
+import Words
+
+### The End ###
Binary file pycaptcha/Captcha/__init__.pyc has changed
Binary file pycaptcha/Captcha/data/.DS_Store has changed
Binary file pycaptcha/Captcha/data/fonts/.DS_Store has changed
Binary file pycaptcha/Captcha/data/fonts/others/._atari-small.bdf has changed
Binary file pycaptcha/Captcha/data/fonts/others/._cursive.bdf has changed
Binary file pycaptcha/Captcha/data/fonts/others/._radon-wide.bdf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/others/CIDFnmap	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,10 @@
+/Dotum-Bold (/usr/share/fonts/truetype/unfonts/UnDotumBold.ttf) /Adobe-Korea1-Unicode ;
+/ZenHei (/usr/share/fonts/truetype/wqy/wqy-zenhei.ttf) /Adobe-GB1-Unicode ;
+/Batang-Regular (/usr/share/fonts/truetype/unfonts/UnBatang.ttf) /Adobe-Korea1-Unicode ;
+/VL-PGothic-Regular (/usr/share/fonts/truetype/vlgothic/VL-PGothic-Regular.ttf) /Adobe-Japan1-Unicode ;
+/Dotum-Regular (/usr/share/fonts/truetype/unfonts/UnDotum.ttf) /Adobe-Korea1-Unicode ;
+/VL-Gothic-Regular-JaH (/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf) /Adobe-Japan2-Unicode ;
+/VL-Gothic-Regular (/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf) /Adobe-Japan1-Unicode ;
+/VL-PGothic-Regular-JaH (/usr/share/fonts/truetype/vlgothic/VL-PGothic-Regular.ttf) /Adobe-Japan2-Unicode ;
+/ZenHei-CNS (/usr/share/fonts/truetype/wqy/wqy-zenhei.ttf) /Adobe-CNS1-Unicode ;
+/Batang-Bold (/usr/share/fonts/truetype/unfonts/UnBatangBold.ttf) /Adobe-Korea1-Unicode ;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/others/FAPIfontmap	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,155 @@
+/Garuda-Oblique << /Path (/usr/share/fonts/truetype/thai/Garuda-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Sans << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstOne << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstOne.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Vemana2000 << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/Vemana.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSerif-Bold << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypo-Bold << /Path (/usr/share/fonts/truetype/thai/TlwgTypo-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSerif-BoldItalic << /Path (/usr/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush-Oblique << /Path (/usr/share/fonts/truetype/thai/Umpush-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationMono-Italic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Italic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Malige << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/Malige-b.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Loma-Oblique << /Path (/usr/share/fonts/truetype/thai/Loma-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstBook << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstBook.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi-BoldItalic << /Path (/usr/share/fonts/truetype/thai/Norasi-BoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Sans-Bold << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Norasi-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeMono-BoldOblique << /Path (/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Serif << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstOffice << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstOffice.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypist-Oblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypist-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSans-Italic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Italic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Waree-Oblique << /Path (/usr/share/fonts/truetype/thai/Waree-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationMono-BoldItalic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationMono-BoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstFarsi << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstFarsi.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgMono-Oblique << /Path (/usr/share/fonts/truetype/thai/TlwgMono-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Garuda-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Garuda-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSans-BoldOblique << /Path (/usr/share/fonts/truetype/freefont/FreeSansBoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/utkal << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/utkal.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSerif-Italic << /Path (/usr/share/fonts/truetype/freefont/FreeSerifItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypist-Bold << /Path (/usr/share/fonts/truetype/thai/TlwgTypist-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSerif-Italic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Italic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Sawasdee-BoldOblique << /Path (/usr/share/fonts/truetype/thai/SawasdeeBoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Umpush-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/cmex10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/cmex10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeMono-Bold << /Path (/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi-Bold << /Path (/usr/share/fonts/truetype/thai/Norasi-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSans-Regular << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Regular.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Loma << /Path (/usr/share/fonts/truetype/thai/Loma.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/wasy10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/wasy10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari-BoldItalic << /Path (/usr/share/fonts/truetype/thai/Kinnari-BoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstNaskh << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstNaskh.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSans-Bold << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Waree << /Path (/usr/share/fonts/truetype/thai/Waree.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Garuda << /Path (/usr/share/fonts/truetype/thai/Garuda.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/cmsy10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/cmsy10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypist-BoldOblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypist-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Sawasdee-Bold << /Path (/usr/share/fonts/truetype/thai/SawasdeeBold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Purisa << /Path (/usr/share/fonts/truetype/thai/Purisa.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstPoster << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstPoster.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSans-Oblique << /Path (/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypo-BoldOblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypo-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Lohit-Punjabi << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_pa.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Waree-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Waree-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypewriter-BoldOblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypewriter-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Garuda-Bold << /Path (/usr/share/fonts/truetype/thai/Garuda-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/RachanaMedium << /Path (/usr/share/fonts/truetype/ttf-malayalam-fonts/Rachana_04.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstArt << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstArt.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationMono-Bold << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypo-Oblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypo-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSerif-Bold << /Path (/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSerif-BoldItalic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-BoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstDecorative << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstDecorative.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Lohit-Hindi << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_hi.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush-LightOblique << /Path (/usr/share/fonts/truetype/thai/Umpush-LightOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSerif-Medium << /Path (/usr/share/fonts/truetype/freefont/FreeSerif.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/mry_KacstQurn << /Path (/usr/share/fonts/truetype/ttf-kacst/mry_KacstQurn.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstDigital << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstDigital.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Sans-Mono-Bold << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Lohit-Gujarati << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_gu.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationMono-Regular << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstLetter << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstLetter.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypo << /Path (/usr/share/fonts/truetype/thai/TlwgTypo.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/msbm10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/msbm10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgMono-Bold << /Path (/usr/share/fonts/truetype/thai/TlwgMono-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Sans-Mono << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi-Italic << /Path (/usr/share/fonts/truetype/thai/Norasi-Italic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstTitleL << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstTitleL.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypewriter << /Path (/usr/share/fonts/truetype/thai/TlwgTypewriter.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeMono-Medium << /Path (/usr/share/fonts/truetype/freefont/FreeMono.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi-Oblique << /Path (/usr/share/fonts/truetype/thai/Norasi-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypewriter-Oblique << /Path (/usr/share/fonts/truetype/thai/TlwgTypewriter-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Phetsarath << /Path (/usr/share/fonts/truetype/ttf-lao/Phetsarath_OT.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/mukti << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/MuktiNarrow.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Sawasdee-Oblique << /Path (/usr/share/fonts/truetype/thai/SawasdeeOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/cmr10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/cmr10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush-Light << /Path (/usr/share/fonts/truetype/thai/Umpush-Light.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush-Bold << /Path (/usr/share/fonts/truetype/thai/Umpush-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/DejaVu-Serif-Bold << /Path (/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstTitle << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstTitle.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Norasi << /Path (/usr/share/fonts/truetype/thai/Norasi.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari-Oblique << /Path (/usr/share/fonts/truetype/thai/Kinnari-Oblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/muktinarrow << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/MuktiNarrowBold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari-Italic << /Path (/usr/share/fonts/truetype/thai/Kinnari-Italic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/kacstPen << /Path (/usr/share/fonts/truetype/ttf-kacst/kacstPen.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Kinnari-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypewriter-Bold << /Path (/usr/share/fonts/truetype/thai/TlwgTypewriter-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeMono-Oblique << /Path (/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSans-Medium << /Path (/usr/share/fonts/truetype/freefont/FreeSans.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSerif-Regular << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Regular.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Umpush << /Path (/usr/share/fonts/truetype/thai/Umpush.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Sawasdee << /Path (/usr/share/fonts/truetype/thai/Sawasdee.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgMono << /Path (/usr/share/fonts/truetype/thai/TlwgMono.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstQurn << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstQurn.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari << /Path (/usr/share/fonts/truetype/thai/Kinnari.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgMono-BoldOblique << /Path (/usr/share/fonts/truetype/thai/TlwgMono-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/KacstScreen << /Path (/usr/share/fonts/truetype/ttf-kacst/KacstScreen.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/FreeSans-Bold << /Path (/usr/share/fonts/truetype/freefont/FreeSansBold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/msam10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/msam10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/cmmi10 << /Path (/usr/share/fonts/truetype/latex-xft-fonts/cmmi10.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Lohit-Tamil << /Path (/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_ta.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/TlwgTypist << /Path (/usr/share/fonts/truetype/thai/TlwgTypist.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Waree-Bold << /Path (/usr/share/fonts/truetype/thai/Waree-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Kinnari-Bold << /Path (/usr/share/fonts/truetype/thai/Kinnari-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Loma-Bold << /Path (/usr/share/fonts/truetype/thai/Loma-Bold.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/LiberationSans-BoldItalic << /Path (/usr/share/fonts/truetype/ttf-liberation/LiberationSans-BoldItalic.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Loma-BoldOblique << /Path (/usr/share/fonts/truetype/thai/Loma-BoldOblique.ttf) /FontType 1 /FAPI /FreeType /SubfontId 0 >> ;
+/Palatino-Italic /URWPalladioL-Ital ; 
+/Palatino-Bold /URWPalladioL-Bold ; 
+/AvantGarde-BookOblique /URWGothicL-BookObli ; 
+/Times-Bold /NimbusRomNo9L-Medi ; 
+/HelveticaNarrow-BoldOblique /NimbusSanL-BoldCondItal ; 
+/Times-Roman /NimbusRomNo9L-Regu ; 
+/NewCenturySchlbk-Italic /CenturySchL-Ital ; 
+/HelveticaNarrow /NimbusSanL-ReguCond ; 
+/Helvetica-Narrow-Bold /NimbusSanL-BoldCond ; 
+/Bookman-Light /URWBookmanL-Ligh ; 
+/Palatino-BoldItalic /URWPalladioL-BoldItal ; 
+/Traditional /KacstBook ; 
+/Times-BoldItalic /NimbusRomNo9L-MediItal ; 
+/AvantGarde-Book /URWGothicL-Book ; 
+/AvantGarde-DemiOblique /URWGothicL-DemiObli ; 
+/Helvetica-Narrow-Oblique /NimbusSanL-ReguCondItal ; 
+/Helvetica-Bold /NimbusSanL-Bold ; 
+/Courier-Oblique /NimbusMonL-ReguObli ; 
+/Times-Italic /NimbusRomNo9L-ReguItal ; 
+/Courier /NimbusMonL-Regu ; 
+/Bookman-Demi /URWBookmanL-DemiBold ; 
+/Helvetica-BoldOblique /NimbusSanL-BoldItal ; 
+/Helvetica-Oblique /NimbusSanL-ReguItal ; 
+/HelveticaNarrow-Oblique /NimbusSanL-ReguCondItal ; 
+/NewCenturySchlbk-BoldItalic /CenturySchL-BoldItal ; 
+/Courier-BoldOblique /NimbusMonL-BoldObli ; 
+/HelveticaNarrow-Bold /NimbusSanL-BoldCond ; 
+/AvantGarde-Demi /URWGothicL-Demi ; 
+/Bookman-LightItalic /URWBookmanL-LighItal ; 
+/ZapfDingbats /Dingbats ; 
+/Helvetica-Narrow-BoldOblique /NimbusSanL-BoldCondItal ; 
+/ZapfChancery-MediumItalic /URWChanceryL-MediItal ; 
+/Helvetica /NimbusSanL-Regu ; 
+/Bookman-DemiItalic /URWBookmanL-DemiBoldItal ; 
+/Palatino-Roman /URWPalladioL-Roma ; 
+/NewCenturySchlbk-Bold /CenturySchL-Bold ; 
+/NewCenturySchlbk-Roman /CenturySchL-Roma ; 
+/Courier-Bold /NimbusMonL-Bold ; 
+/Arabic /KacstBook ; 
+/Helvetica-Narrow /NimbusSanL-ReguCond ; 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/others/Fontmap	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,116 @@
+/LMTypewriter10-CapsOblique (lmtcso10.pfb) ;
+/Dingbats (d050000l.pfb) ;
+/URWBookmanL-DemiBoldItal (b018035l.pfb) ;
+/LMSansQuotation8-Bold (lmssqbx8.pfb) ;
+/Symbol (Symbol.pfb) ;
+/LMTypewriterVarWd10-DarkOblique (lmvtko10.pfb) ;
+/LMRoman10-Demi (lmb10.pfb) ;
+/URWPalladioL-Ital (p052023l.pfb) ;
+/LMTypewriter10-DarkOblique (lmtko10.pfb) ;
+/NimbusSanL-Regu (n019003l.pfb) ;
+/LMTypewriter10-Italic (lmtti10.pfb) ;
+/LMSansQuotation8-BoldOblique (lmssqbo8.pfb) ;
+/URWPalladioL-Roma (p052003l.pfb) ;
+/LMTypewriterVarWd10-Light (lmvtl10.pfb) ;
+/NimbusRomNo9L-Medi (n021004l.pfb) ;
+/NimbusSanL-ReguItal (n019023l.pfb) ;
+/NimbusMonL-Regu (n022003l.pfb) ;
+/LMSans10-Bold (lmssbx10.pfb) ;
+/LMRoman10-CapsOblique (lmcsco10.pfb) ;
+/CenturySchL-Roma (c059013l.pfb) ;
+/URWGothicL-BookObli (a010033l.pfb) ;
+/LMTypewriter10-LightCondensedOblique (lmtlco10.pfb) ;
+/LMSans10-DemiCondensedOblique (lmssdo10.pfb) ;
+/LMRoman10-CapsRegular (lmcsc10.pfb) ;
+/CenturySchL-BoldItal (c059036l.pfb) ;
+/LMRoman10-DemiOblique (lmbo10.pfb) ;
+/LMRoman10-Unslanted (lmu10.pfb) ;
+/LMRoman10-Bold (lmbx10.pfb) ;
+/LMSans10-DemiCondensed (lmssdc10.pfb) ;
+/URWChanceryL-MediItal (z003034l.pfb) ;
+/URWGothicL-DemiObli (a010035l.pfb) ;
+/LMTypewriterVarWd10-Oblique (lmvtto10.pfb) ;
+/NimbusMonL-Bold (n022004l.pfb) ;
+/LMTypewriter10-Oblique (lmtto10.pfb) ;
+/LMRoman10-BoldItalic (lmbxi10.pfb) ;
+/NimbusSanL-ReguCond (n019043l.pfb) ;
+/CenturySchL-Bold (c059016l.pfb) ;
+/LMTypewriterVarWd10-Regular (lmvtt10.pfb) ;
+/URWBookmanL-Ligh (b018012l.pfb) ;
+/LMSansQuotation8-Regular (lmssq8.pfb) ;
+/LMSans10-Regular (lmss10.pfb) ;
+/LMSans10-Oblique (lmsso10.pfb) ;
+/NimbusSanL-BoldCond (n019044l.pfb) ;
+/LMRoman10-Regular (lmr10.pfb) ;
+/LMTypewriter10-LightCondensed (lmtlc10.pfb) ;
+/LMTypewriterVarWd10-Dark (lmvtk10.pfb) ;
+/LMTypewriter10-CapsRegular (lmtcsc10.pfb) ;
+/LMSansQuotation8-Oblique (lmssqo8.pfb) ;
+/StandardSymL (s050000l.pfb) ;
+/NimbusRomNo9L-Regu (n021003l.pfb) ;
+/LMTypewriterVarWd10-LightOblique (lmvtlo10.pfb) ;
+/URWPalladioL-BoldItal (p052024l.pfb) ;
+/CenturySchL-Ital (c059033l.pfb) ;
+/LMRoman10-Dunhill (lmdunh10.pfb) ;
+/URWPalladioL-Bold (p052004l.pfb) ;
+/URWGothicL-Book (a010013l.pfb) ;
+/LMTypewriter10-Dark (lmtk10.pfb) ;
+/NimbusSanL-BoldItal (n019024l.pfb) ;
+/URWGothicL-Demi (a010015l.pfb) ;
+/LMTypewriter10-LightOblique (lmtlo10.pfb) ;
+/LMTypewriter10-Light (lmtl10.pfb) ;
+/NimbusSanL-BoldCondItal (n019064l.pfb) ;
+/LMRoman10-Italic (lmri10.pfb) ;
+/LMRoman10-DunhillOblique (lmduno10.pfb) ;
+/NimbusMonL-ReguObli (n022023l.pfb) ;
+/LMRoman10-Oblique (lmro10.pfb) ;
+/NimbusSanL-ReguCondItal (n019063l.pfb) ;
+/NimbusRomNo9L-MediItal (n021024l.pfb) ;
+/LMRoman10-BoldOblique (lmbxo10.pfb) ;
+/URWBookmanL-DemiBold (b018015l.pfb) ;
+/NimbusSanL-Bold (n019004l.pfb) ;
+/LMSans10-BoldOblique (lmssbo10.pfb) ;
+/URWBookmanL-LighItal (b018032l.pfb) ;
+/NimbusMonL-BoldObli (n022024l.pfb) ;
+/NimbusRomNo9L-ReguItal (n021023l.pfb) ;
+/LMTypewriter10-Regular (lmtt10.pfb) ;
+/Palatino-Italic /URWPalladioL-Ital ; 
+/Palatino-Bold /URWPalladioL-Bold ; 
+/AvantGarde-BookOblique /URWGothicL-BookObli ; 
+/Times-Bold /NimbusRomNo9L-Medi ; 
+/HelveticaNarrow-BoldOblique /NimbusSanL-BoldCondItal ; 
+/Times-Roman /NimbusRomNo9L-Regu ; 
+/NewCenturySchlbk-Italic /CenturySchL-Ital ; 
+/HelveticaNarrow /NimbusSanL-ReguCond ; 
+/Helvetica-Narrow-Bold /NimbusSanL-BoldCond ; 
+/Bookman-Light /URWBookmanL-Ligh ; 
+/Palatino-BoldItalic /URWPalladioL-BoldItal ; 
+/Traditional /KacstBook ; 
+/Times-BoldItalic /NimbusRomNo9L-MediItal ; 
+/AvantGarde-Book /URWGothicL-Book ; 
+/AvantGarde-DemiOblique /URWGothicL-DemiObli ; 
+/Helvetica-Narrow-Oblique /NimbusSanL-ReguCondItal ; 
+/Helvetica-Bold /NimbusSanL-Bold ; 
+/Courier-Oblique /NimbusMonL-ReguObli ; 
+/Times-Italic /NimbusRomNo9L-ReguItal ; 
+/Courier /NimbusMonL-Regu ; 
+/Bookman-Demi /URWBookmanL-DemiBold ; 
+/Helvetica-BoldOblique /NimbusSanL-BoldItal ; 
+/Helvetica-Oblique /NimbusSanL-ReguItal ; 
+/HelveticaNarrow-Oblique /NimbusSanL-ReguCondItal ; 
+/NewCenturySchlbk-BoldItalic /CenturySchL-BoldItal ; 
+/Courier-BoldOblique /NimbusMonL-BoldObli ; 
+/HelveticaNarrow-Bold /NimbusSanL-BoldCond ; 
+/AvantGarde-Demi /URWGothicL-Demi ; 
+/Bookman-LightItalic /URWBookmanL-LighItal ; 
+/ZapfDingbats /Dingbats ; 
+/Helvetica-Narrow-BoldOblique /NimbusSanL-BoldCondItal ; 
+/ZapfChancery-MediumItalic /URWChanceryL-MediItal ; 
+/Helvetica /NimbusSanL-Regu ; 
+/Bookman-DemiItalic /URWBookmanL-DemiBoldItal ; 
+/Palatino-Roman /URWPalladioL-Roma ; 
+/NewCenturySchlbk-Bold /CenturySchL-Bold ; 
+/NewCenturySchlbk-Roman /CenturySchL-Roma ; 
+/Courier-Bold /NimbusMonL-Bold ; 
+/Arabic /KacstBook ; 
+/Helvetica-Narrow /NimbusSanL-ReguCond ; 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/others/cidfmap	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,10 @@
+/Dotum-Bold << /FileType /TrueType /Path (/usr/share/fonts/truetype/unfonts/UnDotumBold.ttf) /SubfontID 0 /CSI [(Korea1) 0] >> ;
+/ZenHei << /FileType /TrueType /Path (/usr/share/fonts/truetype/wqy/wqy-zenhei.ttf) /SubfontID 0 /CSI [(GB1) 0] >> ;
+/Batang-Regular << /FileType /TrueType /Path (/usr/share/fonts/truetype/unfonts/UnBatang.ttf) /SubfontID 0 /CSI [(Korea1) 0] >> ;
+/VL-PGothic-Regular << /FileType /TrueType /Path (/usr/share/fonts/truetype/vlgothic/VL-PGothic-Regular.ttf) /SubfontID 0 /CSI [(Japan1) 0] >> ;
+/Dotum-Regular << /FileType /TrueType /Path (/usr/share/fonts/truetype/unfonts/UnDotum.ttf) /SubfontID 0 /CSI [(Korea1) 0] >> ;
+/VL-Gothic-Regular-JaH << /FileType /TrueType /Path (/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf) /SubfontID 0 /CSI [(Japan2) 0] >> ;
+/VL-Gothic-Regular << /FileType /TrueType /Path (/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf) /SubfontID 0 /CSI [(Japan1) 0] >> ;
+/VL-PGothic-Regular-JaH << /FileType /TrueType /Path (/usr/share/fonts/truetype/vlgothic/VL-PGothic-Regular.ttf) /SubfontID 0 /CSI [(Japan2) 0] >> ;
+/ZenHei-CNS << /FileType /TrueType /Path (/usr/share/fonts/truetype/wqy/wqy-zenhei.ttf) /SubfontID 0 /CSI [(CNS1) 0] >> ;
+/Batang-Bold << /FileType /TrueType /Path (/usr/share/fonts/truetype/unfonts/UnBatangBold.ttf) /SubfontID 0 /CSI [(Korea1) 0] >> ;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/vera/COPYRIGHT.TXT	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,124 @@
+Bitstream Vera Fonts Copyright
+
+The fonts have a generous copyright, allowing derivative works (as
+long as "Bitstream" or "Vera" are not in the names), and full
+redistribution (so long as they are not *sold* by themselves). They
+can be be bundled, redistributed and sold with any software.
+
+The fonts are distributed under the following copyright:
+
+Copyright
+=========
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
+Vera is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute
+the Font Software, including without limitation the rights to use,
+copy, merge, publish, distribute, and/or sell copies of the Font
+Software, and to permit persons to whom the Font Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Bitstream" or the word "Vera".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Bitstream Vera" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
+SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font
+Software without prior written authorization from the Gnome Foundation
+or Bitstream Inc., respectively. For further information, contact:
+fonts at gnome dot org.
+
+Copyright FAQ
+=============
+
+   1. I don't understand the resale restriction... What gives?
+
+      Bitstream is giving away these fonts, but wishes to ensure its
+      competitors can't just drop the fonts as is into a font sale system
+      and sell them as is. It seems fair that if Bitstream can't make money
+      from the Bitstream Vera fonts, their competitors should not be able to
+      do so either. You can sell the fonts as part of any software package,
+      however.
+
+   2. I want to package these fonts separately for distribution and
+      sale as part of a larger software package or system.  Can I do so?
+
+      Yes. A RPM or Debian package is a "larger software package" to begin 
+      with, and you aren't selling them independently by themselves. 
+      See 1. above.
+
+   3. Are derivative works allowed?
+      Yes!
+
+   4. Can I change or add to the font(s)?
+      Yes, but you must change the name(s) of the font(s).
+
+   5. Under what terms are derivative works allowed?
+
+      You must change the name(s) of the fonts. This is to ensure the
+      quality of the fonts, both to protect Bitstream and Gnome. We want to
+      ensure that if an application has opened a font specifically of these
+      names, it gets what it expects (though of course, using fontconfig,
+      substitutions could still could have occurred during font
+      opening). You must include the Bitstream copyright. Additional
+      copyrights can be added, as per copyright law. Happy Font Hacking!
+
+   6. If I have improvements for Bitstream Vera, is it possible they might get 
+       adopted in future versions?
+
+      Yes. The contract between the Gnome Foundation and Bitstream has
+      provisions for working with Bitstream to ensure quality additions to
+      the Bitstream Vera font family. Please contact us if you have such
+      additions. Note, that in general, we will want such additions for the
+      entire family, not just a single font, and that you'll have to keep
+      both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
+      glyphs to the font, they must be stylistically in keeping with Vera's
+      design. Vera cannot become a "ransom note" font. Jim Lyles will be
+      providing a document describing the design elements used in Vera, as a
+      guide and aid for people interested in contributing to Vera.
+
+   7. I want to sell a software package that uses these fonts: Can I do so?
+
+      Sure. Bundle the fonts with your software and sell your software
+      with the fonts. That is the intent of the copyright.
+
+   8. If applications have built the names "Bitstream Vera" into them, 
+      can I override this somehow to use fonts of my choosing?
+
+      This depends on exact details of the software. Most open source
+      systems and software (e.g., Gnome, KDE, etc.) are now converting to
+      use fontconfig (see www.fontconfig.org) to handle font configuration,
+      selection and substitution; it has provisions for overriding font
+      names and subsituting alternatives. An example is provided by the
+      supplied local.conf file, which chooses the family Bitstream Vera for
+      "sans", "serif" and "monospace".  Other software (e.g., the XFree86
+      core server) has other mechanisms for font substitution.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/vera/README.TXT	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,11 @@
+Contained herin is the Bitstream Vera font family.
+
+The Copyright information is found in the COPYRIGHT.TXT file (along
+with being incoporated into the fonts themselves).
+
+The releases notes are found in the file "RELEASENOTES.TXT".
+
+We hope you enjoy Vera!
+
+                        Bitstream, Inc.
+			The Gnome Project
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/vera/RELEASENOTES.TXT	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,162 @@
+Bitstream Vera Fonts - April 16, 2003
+=====================================
+
+The version number of these fonts is 1.10 to distinguish them from the
+beta test fonts.
+
+Note that the Vera copyright is incorporated in the fonts themselves.
+The License field in the fonts contains the copyright license as it
+appears below. The TrueType copyright field is not large enough to
+contain the full license, so the license is incorporated (as you might
+think if you thought about it) into the license field, which
+unfortunately can be obscure to find.  (In pfaedit, see: Element->Font
+Info->TTFNames->License).
+
+Our apologies for it taking longer to complete the fonts than planned.
+Beta testers requested a tighter line spacing (less leading) and Jim
+Lyles redesigned Vera's accents to bring its line spacing to more
+typical of other fonts.  This took additional time and effort.  Our
+thanks to Jim for this effort above and beyond the call of duty.
+
+There are four monospace and sans faces (normal, oblique, bold, bold
+oblique) and two serif faces (normal and bold). Fontconfig/Xft2 (see
+www.fontconfig.org) can artificially oblique the serif faces for you:
+this loses hinting and distorts the faces slightly, but is visibly
+different than normal and bold, and reasonably pleasing.
+
+On systems with fontconfig 2.0 or 2.1 installed, making your sans,
+serif and monospace fonts default to these fonts is very easy.  Just
+drop the file local.conf into your /etc/fonts directory.  This will
+make the Bitstream fonts your default fonts for all applications using
+fontconfig (if sans, serif, or monospace names are used, as they often
+are as default values in many desktops). The XML in local.conf may
+need modification to enable subpixel decimation, if appropriate,
+however, the commented out phrase does so for XFree86 4.3, in the case
+that the server does not have sufficient information to identify the
+use of a flat panel.  Fontconfig 2.2 adds Vera to the list of font
+families and will, by default use it as the default sans, serif and
+monospace fonts.
+
+During the testing of the final Vera fonts, we learned that screen
+fonts in general are only typically hinted to work correctly at
+integer pixel sizes.  Vera is coded internally for integer sizes only.
+We need to investigate further to see if there are commonly used fonts
+that are hinted to be rounded but are not rounded to integer sizes due
+to oversights in their coding.
+
+Most fonts work best at 8 pixels and below if anti-aliased only, as
+the amount of work required to hint well at smaller and smaller sizes
+becomes astronomical.  GASP tables are typically used to control
+whether hinting is used or not, but Freetype/Xft does not currently
+support GASP tables (which are present in Vera).
+
+To mitigate this problem, both for Vera and other fonts, there will be
+(very shortly) a new fontconfig 2.2 release that will, by default not
+apply hints if the size is below 8 pixels. if you should have a font
+that in fact has been hinted more agressively, you can use fontconfig
+to note this exception. We believe this should improve many hinted
+fonts in addition to Vera, though implemeting GASP support is likely
+the right long term solution.
+
+Font rendering in Gnome or KDE is the combination of algorithms in
+Xft2 and Freetype, along with hinting in the fonts themselves. It is
+vital to have sufficient information to disentangle problems that you
+may observe.
+
+Note that having your font rendering system set up correctly is vital
+to proper judgement of problems of the fonts:
+
+    * Freetype may or may not be configured to in ways that may
+      implement execution of possibly patented (in some parts of the world)
+      TrueType hinting algorithms, particularly at small sizes.  Best
+      results are obtained while using these algorithms.
+
+    * The freetype autohinter (used when the possibly patented
+      algorithms are not used) continues to improve with each release. If
+      you are using the autohinter, please ensure you are using an up to
+      date version of freetype before reporting problems.
+
+    * Please identify what version of freetype you are using in any
+      bug reports, and how your freetype is configured.
+
+    * Make sure you are not using the freetype version included in
+      XFree86 4.3, as it has bugs that significantly degrade most fonts,
+      including Vera. if you build XFree86 4.3 from source yourself, you may
+      have installed this broken version without intending it (as I
+      did). Vera was verified with the recently released Freetype 2.1.4. On
+      many systems, 'ldd" can be used to see which freetype shared library
+      is actually being used.
+
+    * Xft/X Render does not (yet) implement gamma correction.  This
+      causes significant problems rendering white text on a black background
+      (causing partial pixels to be insufficiently shaded) if the gamma of
+      your monitor has not been compensated for, and minor problems with
+      black text on a while background.  The program "xgamma" can be used to
+      set a gamma correction value in the X server's color pallette. Most
+      monitors have a gamma near 2.
+
+    * Note that the Vera family uses minimal delta hinting. Your
+      results on other systems when not used anti-aliased may not be
+      entirely satisfying. We are primarily interested in reports of
+      problems on open source systems implementing Xft2/fontconfig/freetype
+      (which implements antialiasing and hinting adjustements, and
+      sophisticated subpixel decimation on flatpanels).  Also, the
+      algorithms used by Xft2 adjust the hints to integer widths and the
+      results are crisper on open source systems than on Windows or
+      MacIntosh.
+
+    * Your fontconfig may (probably does) predate the release of
+      fontconfig 2.2, and you may see artifacts not present when the font is
+      used at very small sizes with hinting enabled. "vc-list -V" can be
+      used to see what version you have installed.
+
+We believe and hope that these fonts will resolve the problems
+reported during beta test.  The largest change is the reduction of
+leading (interline spacing), which had annoyed a number of people, and
+reduced Vera's utility for some applcations.  The Vera monospace font
+should also now make '0' and 'O' and '1' and 'l' more clearly
+distinguishable.
+
+The version of these fonts is version 1.10.  Fontconfig should be
+choosing the new version of the fonts if both the released fonts and
+beta test fonts are installed (though please discard them: they have
+names of form tt20[1-12]gn.ttf).  Note that older versions of
+fontconfig sometimes did not rebuild their cache correctly when new
+fonts are installed: please upgrade to fontconfig 2.2. "fc-cache -f"
+can be used to force rebuilding fontconfig's cache files.
+
+If you note problems, please send them to fonts at gnome dot org, with
+exactly which face and size and unicode point you observe the problem
+at. The xfd utility from XFree86 CVS may be useful for this (e.g. "xfd
+-fa sans").  A possibly more useful program to examine fonts at a
+variety of sizes is the "waterfall" program found in Keith Packard's
+CVS.
+
+        $ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS login
+        Logging in to :pserver:anoncvs@keithp.com:2401/local/src/CVS
+        CVS password: <hit return>
+        $ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS co waterfall
+        $ cd waterfall
+        $ xmkmf -a
+        $ make
+        # make install
+        # make install.man
+
+Again, please make sure you are running an up-to-date freetype, and
+that you are only examining integer sizes.
+
+Reporting Problems
+==================
+
+Please send problem reports to fonts at gnome org, with the following
+information:
+
+   1. Version of Freetype, Xft2 and fontconfig
+   2. Whether TT hinting is being used, or the autohinter
+   3. Application being used
+   4. Character/Unicode code point that has problems (if applicable)
+   5. Version of which operating system
+   6. Please include a screenshot, when possible.
+
+Please check the fonts list archives before reporting problems to cut
+down on duplication.
Binary file pycaptcha/Captcha/data/fonts/vera/Vera.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraBI.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraBd.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraIt.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraMoBI.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraMoBd.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraMoIt.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraMono.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraSe.ttf has changed
Binary file pycaptcha/Captcha/data/fonts/vera/VeraSeBd.ttf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/fonts/vera/local.conf	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<!-- /etc/fonts.conf file to configure system font access -->
+<fontconfig>
+        <!--  Enable sub-pixel rendering
+        <match target="font">
+                <test qual="all" name="rgba">
+                        <const>unknown</const>
+                </test>
+                <edit name="rgba" mode="assign"><const>rgb</const></edit>
+        </match>
+         -->
+
+        <alias>
+                <family>serif</family>
+                <prefer>
+                        <family>Bitstream Vera Serif</family>
+                </prefer>
+        </alias>
+        <alias>
+                <family>sans-serif</family>
+                <prefer>
+                        <family>Bitstream Vera Sans</family>
+                </prefer>
+        </alias>
+        <alias>
+                <family>monospace</family>
+                <prefer>
+                        <family>Bitstream Vera Sans Mono</family>
+                </prefer>
+        </alias>
+</fontconfig>
Binary file pycaptcha/Captcha/data/pictures/.DS_Store has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/1.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/10.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/11.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/12.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/2.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/3.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/4.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/5.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/6.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/7.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/8.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/abstract/9.jpeg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/pictures/abstract/README	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,3 @@
+These images were created by the author with Fyre, expressly for PyCAPTCHA.
+
+Copyright (c) 2004 Micah Dowty
Binary file pycaptcha/Captcha/data/pictures/nature/Craig_Barrington_ocotillo_and_mountains.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/nature/Kerry_Carloy_Chisos_Sunset.jpeg has changed
Binary file pycaptcha/Captcha/data/pictures/nature/Paul_Dowty_Mt_Bross.jpeg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/pictures/nature/README	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,2 @@
+These are uncopyrighted images gathered from various sources,
+including the author's family and national park service web sites.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/words/README	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,4 @@
+These word lists are from various sources:
+
+basic-english:
+   http://simple.wikipedia.org/wiki/Basic_English_Alphabetical_Wordlist
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/words/basic-english	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,852 @@
+a
+able
+about
+account
+acid
+across
+act
+addition
+adjustment
+advertisement
+agreement
+after
+again
+against
+air
+all
+almost
+among
+amount
+amusement
+and
+angle
+angry
+animal
+answer
+ant
+any
+apparatus
+apple
+approval
+arch
+argument
+arm
+army
+art
+as
+at
+attack
+attempt
+attention
+attraction
+authority
+automatic
+awake
+baby
+back
+bad
+bag
+balance
+ball
+band
+base
+basin
+basket
+bath
+be
+beautiful
+because
+bed
+bee
+before
+behavior
+belief
+bell
+bent
+berry
+between
+bird
+birth
+bit
+bite
+bitter
+black
+blade
+blood
+blow
+blue
+board
+boat
+body
+boiling
+bone
+book
+boot
+bottle
+box
+boy
+brain
+brake
+branch
+brass
+bread
+breath
+brick
+bridge
+bright
+broken
+brother
+brown
+brush
+bucket
+building
+bulb
+burn
+burst
+business
+but
+butter
+button
+by
+cake
+camera
+canvas
+card
+care
+carriage
+cart
+cat
+cause
+certain
+chain
+chalk
+chance
+change
+cheap
+cheese
+chemical
+chest
+chief
+chin
+church
+circle
+clean
+clear
+clock
+cloth
+cloud
+coal
+coat
+cold
+collar
+color
+comb
+come
+comfort
+committee
+common
+company
+comparison
+competition
+complete
+complex
+condition
+connection
+conscious
+control
+cook
+copper
+copy
+cord
+cork
+cotton
+cough
+country
+cover
+cow
+crack
+credit
+crime
+cruel
+crush
+cry
+cup
+current
+curtain
+curve
+cushion
+cut
+damage
+danger
+dark
+daughter
+day
+dead
+dear
+death
+debt
+decision
+deep
+degree
+delicate
+dependent
+design
+desire
+destruction
+detail
+development
+different
+digestion
+direction
+dirty
+discovery
+discussion
+disease
+disgust
+distance
+distribution
+division
+do
+dog
+door
+down
+doubt
+drain
+drawer
+dress
+drink
+driving
+drop
+dry
+dust
+ear
+early
+earth
+east
+edge
+education
+effect
+egg
+elastic
+electric
+end
+engine
+enough
+equal
+error
+even
+event
+ever
+every
+example
+exchange
+existence
+expansion
+experience
+expert
+eye
+face
+fact
+fall
+false
+family
+far
+farm
+fat
+father
+fear
+feather
+feeble
+feeling
+female
+fertile
+fiction
+field
+fight
+finger
+fire
+first
+fish
+fixed
+flag
+flame
+flat
+flight
+floor
+flower
+fly
+fold
+food
+foolish
+foot
+for
+force
+fork
+form
+forward
+fowl
+frame
+free
+frequent
+friend
+from
+front
+fruit
+full
+future
+garden
+general
+get
+girl
+give
+glass
+glove
+go
+goat
+gold
+good
+government
+grain
+grass
+great
+green
+grey/gray
+grip
+group
+growth
+guide
+gun
+hair
+hammer
+hand
+hanging
+happy
+harbor
+hard
+harmony
+hat
+hate
+have
+he
+head
+healthy
+hearing
+heart
+heat
+help
+here
+high
+history
+hole
+hollow
+hook
+hope
+horn
+horse
+hospital
+hour
+house
+how
+humor
+ice
+idea
+if
+ill
+important
+impulse
+in
+increase
+industry
+ink
+insect
+instrument
+insurance
+interest
+invention
+iron
+island
+jelly
+jewel
+join
+journey
+judge
+jump
+keep
+kettle
+key
+kick
+kind
+kiss
+knee
+knife
+knot
+knowledge
+land
+language
+last
+late
+laugh
+law
+lead
+leaf
+learning
+leather
+left
+leg
+let
+letter
+level
+library
+lift
+light
+like
+limit
+line
+linen
+lip
+liquid
+list
+little
+less
+least
+living
+lock
+long
+loose
+loss
+loud
+love
+low
+machine
+make
+male
+man
+manager
+map
+mark
+market
+married
+match
+material
+mass
+may
+meal
+measure
+meat
+medical
+meeting
+memory
+metal
+middle
+military
+milk
+mind
+mine
+minute
+mist
+mixed
+money
+monkey
+month
+moon
+morning
+mother
+motion
+mountain
+mouth
+move
+much
+more
+most
+muscle
+music
+nail
+name
+narrow
+nation
+natural
+near
+necessary
+neck
+need
+needle
+nerve
+net
+new
+news
+night
+no
+noise
+normal
+north
+nose
+not
+note
+now
+number
+nut
+observation
+of
+off
+offer
+office
+oil
+old
+on
+only
+open
+operation
+opposite
+opinion
+other
+or
+orange
+order
+organization
+ornament
+out
+oven
+over
+owner
+page
+pain
+paint
+paper
+parallel
+parcel
+part
+past
+paste
+payment
+peace
+pen
+pencil
+person
+physical
+picture
+pig
+pin
+pipe
+place
+plane
+plant
+plate
+play
+please
+pleasure
+plough/plow
+pocket
+point
+poison
+polish
+political
+poor
+porter
+position
+possible
+pot
+potato
+powder
+power
+present
+price
+print
+prison
+private
+probable
+process
+produce
+profit
+property
+prose
+protest
+public
+pull
+pump
+punishment
+purpose
+push
+put
+quality
+question
+quick
+quiet
+quite
+rail
+rain
+range
+rat
+rate
+ray
+reaction
+red
+reading
+ready
+reason
+receipt
+record
+regret
+regular
+relation
+religion
+representative
+request
+respect
+responsible
+rest
+reward
+rhythm
+rice
+right
+ring
+river
+road
+rod
+roll
+roof
+room
+root
+rough
+round
+rub
+rule
+run
+sad
+safe
+sail
+salt
+same
+sand
+say
+scale
+school
+science
+scissors
+screw
+sea
+seat
+second
+secret
+secretary
+see
+seed
+selection
+self
+send
+seem
+sense
+separate
+serious
+servant
+sex
+shade
+shake
+shame
+sharp
+sheep
+shelf
+ship
+shirt
+shock
+shoe
+short
+shut
+side
+sign
+silk
+silver
+simple
+sister
+size
+skin
+skirt
+sky
+sleep
+slip
+slope
+slow
+small
+smash
+smell
+smile
+smoke
+smooth
+snake
+sneeze
+snow
+so
+soap
+society
+sock
+soft
+solid
+some
+son
+song
+sort
+sound
+south
+soup
+space
+spade
+special
+sponge
+spoon
+spring
+square
+stamp
+stage
+star
+start
+statement
+station
+steam
+stem
+steel
+step
+stick
+still
+stitch
+stocking
+stomach
+stone
+stop
+store
+story
+strange
+street
+stretch
+sticky
+stiff
+straight
+strong
+structure
+substance
+sugar
+suggestion
+summer
+support
+surprise
+such
+sudden
+sun
+sweet
+swim
+system
+table
+tail
+take
+talk
+tall
+taste
+tax
+teaching
+tendency
+test
+than
+that
+the
+then
+theory
+there
+thick
+thin
+thing
+this
+thought
+thread
+throat
+though
+through
+thumb
+thunder
+ticket
+tight
+tired
+till
+time
+tin
+to
+toe
+together
+tomorrow
+tongue
+tooth
+top
+touch
+town
+trade
+train
+transport
+tray
+tree
+trick
+trousers
+true
+trouble
+turn
+twist
+umbrella
+under
+unit
+use
+up
+value
+verse
+very
+vessel
+view
+violent
+voice
+walk
+wall
+waiting
+war
+warm
+wash
+waste
+watch
+water
+wave
+wax
+way
+weather
+week
+weight
+well
+west
+wet
+wheel
+when
+where
+while
+whip
+whistle
+white
+who
+why
+wide
+will
+wind
+window
+wine
+wing
+winter
+wire
+wise
+with
+woman
+wood
+wool
+word
+work
+worm
+wound
+writing
+wrong
+year
+yellow
+yes
+yesterday
+you
+young
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Captcha/data/words/characters	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,26 @@
+q
+w
+e
+r
+t
+y
+u
+i
+o
+p
+a
+s
+d
+f
+g
+h
+j
+k
+l
+z
+x
+c
+v
+b
+n
+m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Facade.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+
+
+from Captcha.Visual.Tests import PseudoGimpy, AngryGimpy
+import numpy
+
+# Une fonction simple pour generer un captcha
+# ease : represente la difficulte du captcha a generer 
+#      0 = facile et 1 (ou autre chose) = difficile 
+#solution : specifie si on veut en retour un array numpy representant 
+#l image ou un tuple contenant l'array et la solution du captcha.
+
+# Des fontes additionnelles peuvent etre ajoutees au dossier pyCaptcha/Captcha/data/fonts/others
+# Le programme choisit une fonte aleatoirement dans ce dossier ainsi que le dossir vera.
+
+
+def generateCaptcha (ease=0, solution=0):
+
+    if ease == 1:
+        g = AngryGimpy()
+
+    else:
+        g = PseudoGimpy()
+    
+    i = g.render()
+    a = numpy.asarray(i)
+
+    if solution == 0:
+       return a
+
+    else :
+        return (a, g,solutions)
Binary file pycaptcha/Facade.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/Facade.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+
+
+from Captcha.Visual.Tests import PseudoGimpy, AngryGimpy
+import numpy
+
+# Une fonction simple pour generer un captcha
+# ease : represente la difficulte du captcha a generer 
+#      0 = facile et 1 (ou autre chose) = difficile 
+#solution : specifie si on veut en retour un array numpy representant 
+#l image ou un tuple contenant l'array et la solution du captcha.
+
+# Des fontes additionnelles peuvent etre ajoutees au dossier pyCaptcha/Captcha/data/fonts/others
+# Le programme choisit une fonte aleatoirement dans ce dossier ainsi que le dossir vera.
+
+
+def generateCaptcha (ease=0, solution=0):
+
+    if ease == 1:
+        g = AngryGimpy()
+
+    else:
+        g = PseudoGimpy()
+    
+    i = g.render()
+    a = numpy.asarray(i)
+
+    if solution == 0:
+       return a
+
+    else :
+        return (a, g,solutions)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/README	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,51 @@
+======================
+Python CAPTCHA package
+======================
+
+About
+-----
+
+This is the PyCAPTCHA package, a collection of Python modules
+implementing CAPTCHAs: automated tests that humans should pass,
+but current computer programs can't. These tests are often
+used for security.
+
+See  http://www.captcha.net for more information and examples.
+
+This project was started because the CIA project, written in
+Python, needed a CAPTCHA to automate its user creation process
+safely. All existing implementations the author could find were
+written in Java or for the .NET framework, so a simple Python
+alternative was needed.
+
+Examples
+--------
+
+Included are several example programs:
+
+  - simple_example.py is a bare-bones example that just generates
+    and displays an image.
+
+  - http_example.py is a longer example that uses BaseHTTPServer
+    to simulate a CAPTCHA's use in a web environment. Running this
+    example and connecting to it from your web browser is a quick
+    and easy way to see PyCAPTCHA in action
+
+  - modpython_example.py is a version of http_example that runs
+    from an Apache server equipped with a properly configured
+    mod_python.
+
+
+Dependencies
+------------
+
+- Python 2.2.1 or later
+- the Python Imaging Library, required for visual CAPTCHAs
+
+
+Contacts
+--------
+
+Micah Dowty <micah@navi.cx>
+
+'scanline' on irc.freenode.net
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/http_example.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+#
+# An example that presents CAPTCHA tests in a web environment
+# and gives the user a chance to solve them. Run it, optionally
+# specifying a port number on the command line, then point your web
+# browser at the given URL.
+#
+
+from Captcha.Visual import Tests
+from Captcha import Factory
+import BaseHTTPServer, urlparse, sys
+
+
+class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    def do_GET(self):
+        scheme, host, path, parameters, query, fragment = urlparse.urlparse(self.path)
+
+        # Split the path into segments
+        pathSegments = path.split('/')[1:]
+
+        # Split the query into key-value pairs
+        args = {}
+        for pair in query.split("&"):
+            if pair.find("=") >= 0:
+                key, value = pair.split("=", 1)
+                args.setdefault(key, []).append(value)
+            else:
+                args[pair] = []
+
+        # A hack so it works with a proxy configured for VHostMonster :)
+        if pathSegments[0] == "vhost":
+            pathSegments = pathSegments[3:]
+
+        if pathSegments[0] == "":
+            self.handleRootPage(args.get('test', Tests.__all__)[0])
+
+        elif pathSegments[0] == "images":
+            self.handleImagePage(pathSegments[1])
+
+        elif pathSegments[0] == "solutions":
+            self.handleSolutionPage(pathSegments[1], args['word'][0])
+
+        else:
+            self.handle404()
+
+    def handle404(self):
+        self.send_response(404)
+        self.send_header("Content-Type", "text/html")
+        self.end_headers()
+        self.wfile.write("<html><body><h1>No such resource</h1></body></html>")
+
+    def handleRootPage(self, testName):
+        self.send_response(200)
+        self.send_header("Content-Type", "text/html")
+        self.end_headers()
+
+        test = self.captchaFactory.new(getattr(Tests, testName))
+
+        # Make a list of tests other than the one we're using
+        others = []
+        for t in Tests.__all__:
+            if t != testName:
+                others.append('<li><a href="/?test=%s">%s</a></li>' % (t,t))
+        others = "\n".join(others)
+
+        self.wfile.write("""<html>
+<head>
+<title>PyCAPTCHA Example</title>
+</head>
+<body>
+<h1>PyCAPTCHA Example</h1>
+<p>
+  <b>%s</b>:
+  %s
+</p>
+
+<p><img src="/images/%s"/></p>
+<p>
+  <form action="/solutions/%s" method="get">
+    Enter the word shown:
+    <input type="text" name="word"/>
+  </form>
+</p>
+
+<p>
+Or try...
+<ul>
+%s
+</ul>
+</p>
+
+</body>
+</html>
+""" % (test.__class__.__name__, test.__doc__, test.id, test.id, others))
+
+    def handleImagePage(self, id):
+        test = self.captchaFactory.get(id)
+        if not test:
+            return self.handle404()
+
+        self.send_response(200)
+        self.send_header("Content-Type", "image/jpeg")
+        self.end_headers()
+        test.render().save(self.wfile, "JPEG")
+
+    def handleSolutionPage(self, id, word):
+        test = self.captchaFactory.get(id)
+        if not test:
+            return self.handle404()
+
+        if not test.valid:
+            # Invalid tests will always return False, to prevent
+            # random trial-and-error attacks. This could be confusing to a user...
+            result = "Test invalidated, try another test"
+        elif test.testSolutions([word]):
+            result = "Correct"
+        else:
+            result = "Incorrect"
+
+        self.send_response(200)
+        self.send_header("Content-Type", "text/html")
+        self.end_headers()
+        self.wfile.write("""<html>
+<head>
+<title>PyCAPTCHA Example</title>
+</head>
+<body>
+<h1>PyCAPTCHA Example</h1>
+<h2>%s</h2>
+<p><img src="/images/%s"/></p>
+<p><b>%s</b></p>
+<p>You guessed: %s</p>
+<p>Possible solutions: %s</p>
+<p><a href="/">Try again</a></p>
+</body>
+</html>
+""" % (test.__class__.__name__, test.id, result, word, ", ".join(test.solutions)))
+
+
+def main(port):
+    print "Starting server at http://localhost:%d/" % port
+    handler = RequestHandler
+    handler.captchaFactory = Factory()
+    BaseHTTPServer.HTTPServer(('', port), RequestHandler).serve_forever()
+
+if __name__ == "__main__":
+    # The port number can be specified on the command line, default is 8080
+    if len(sys.argv) >= 2:
+        port = int(sys.argv[1])
+    else:
+        port = 8080
+    main(port)
+
+### The End ###
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/modpython_example.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,113 @@
+#
+# An example that presents CAPTCHA tests in a web environment
+# and gives the user a chance to solve them.
+#
+# This example is for use with Apache using mod_python and its
+# Publisher handler. For example, if your apache configuration
+# included something like:
+#
+#   AddHandler python-program .py
+#   PythonHandler mod_python.publisher
+#
+# You could place this script anywhere in your web space to see
+# the demo.
+#
+# --Micah <micah@navi.cx>
+#
+
+from Captcha.Visual import Tests
+import Captcha
+from mod_python import apache
+
+
+def _getFactory(req):
+    return Captcha.PersistentFactory("/tmp/pycaptcha_%s" % req.interpreter)
+
+
+def test(req, name=Tests.__all__[0]):
+    """Show a newly generated CAPTCHA of the given class.
+       Default is the first class name given in Tests.__all__
+       """
+    test = _getFactory(req).new(getattr(Tests, name))
+
+    # Make a list of tests other than the one we're using
+    others = []
+    for t in Tests.__all__:
+        if t != name:
+            others.append('<li><a href="?name=%s">%s</a></li>' % (t,t))
+    others = "\n".join(others)
+
+    return """<html>
+<head>
+<title>PyCAPTCHA Example</title>
+</head>
+<body>
+<h1>PyCAPTCHA Example (for mod_python)</h1>
+<p>
+  <b>%s</b>:
+  %s
+</p>
+
+<p><img src="image?id=%s"/></p>
+<p>
+  <form action="solution" method="get">
+    Enter the word shown:
+    <input type="text" name="word"/>
+    <input type="hidden" name="id" value="%s"/>
+  </form>
+</p>
+
+<p>
+Or try...
+<ul>
+%s
+</ul>
+</p>
+
+</body>
+</html>
+""" % (test.__class__.__name__, test.__doc__, test.id, test.id, others)
+
+
+def image(req, id):
+    """Generate an image for the CAPTCHA with the given ID string"""
+    test = _getFactory(req).get(id)
+    if not test:
+        raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
+    req.content_type = "image/jpeg"
+    test.render().save(req, "JPEG")
+    return apache.OK
+
+
+def solution(req, id, word):
+    """Grade a CAPTCHA given a solution word"""
+    test = _getFactory(req).get(id)
+    if not test:
+        raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
+
+    if not test.valid:
+        # Invalid tests will always return False, to prevent
+        # random trial-and-error attacks. This could be confusing to a user...
+        result = "Test invalidated, try another test"
+    elif test.testSolutions([word]):
+        result = "Correct"
+    else:
+        result = "Incorrect"
+
+    return """<html>
+<head>
+<title>PyCAPTCHA Example</title>
+</head>
+<body>
+<h1>PyCAPTCHA Example</h1>
+<h2>%s</h2>
+<p><img src="image?id=%s"/></p>
+<p><b>%s</b></p>
+<p>You guessed: %s</p>
+<p>Possible solutions: %s</p>
+<p><a href="test">Try again</a></p>
+</body>
+</html>
+""" % (test.__class__.__name__, test.id, result, word, ", ".join(test.solutions))
+
+### The End ###
Binary file pycaptcha/output.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/setup.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+from distutils.core import setup
+from setup.my_install_data import *
+
+setup (name = "PyCAPTCHA",
+       version = "0.4",
+       description = "A Python framework for CAPTCHA tests",
+       maintainer = "Micah Dowty",
+       maintainer_email = "micah@navi.cx",
+       license = "MIT",
+       packages = [
+           'Captcha',
+           'Captcha.Visual',
+       ],
+       cmdclass = {
+           'install_data': my_install_data,
+       },
+       data_files = [Data_Files(
+           preserve_path = 1,
+           base_dir      = 'install_lib',
+           copy_to       = 'Captcha/data',
+           strip_dirs    = 2,
+           template      = [
+               'graft Captcha/data',
+           ],
+       )],
+       )
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/setup/__init__.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,1 @@
+# Extra modules for use with distutils
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/setup/my_install_data.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,191 @@
+"""my_install_data.py
+
+Provides a more sophisticated facility to install data files
+than distutils' install_data does.
+You can specify your files as a template like in MANIFEST.in
+and you have more control over the copy process.
+
+Copyright 2000 by Rene Liebscher, Germany.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Note:
+This licence is only for this file.
+PyOpenGL has its own licence. (it is almost identical.)
+"""
+
+# created 2000/08/01, Rene Liebscher <R.Liebscher@gmx.de>
+
+###########################################################################
+# import some modules we need
+
+import os,sys,string
+from types import StringType,TupleType,ListType
+from distutils.util import change_root
+from distutils.filelist import FileList
+from distutils.command.install_data import install_data
+
+###########################################################################
+# a container class for our more sophisticated install mechanism
+
+class Data_Files:
+    """ container for list of data files.
+        supports alternate base_dirs e.g. 'install_lib','install_header',...
+        supports a directory where to copy files
+        supports templates as in MANIFEST.in
+        supports preserving of paths in filenames
+            eg. foo/xyz is copied to base_dir/foo/xyz
+        supports stripping of leading dirs of source paths
+            eg. foo/bar1/xyz, foo/bar2/abc can be copied to bar1/xyz, bar2/abc
+    """
+
+    def __init__(self,base_dir=None,files=None,copy_to=None,template=None,preserve_path=0,strip_dirs=0):
+        self.base_dir = base_dir
+        self.files = files
+        self.copy_to = copy_to
+        self.template = template
+        self.preserve_path = preserve_path
+        self.strip_dirs = strip_dirs
+        self.finalized = 0
+
+    def warn (self, msg):
+        sys.stderr.write ("warning: %s: %s\n" %
+                          ("install_data", msg))
+
+    def debug_print (self, msg):
+        """Print 'msg' to stdout if the global DEBUG (taken from the
+        DISTUTILS_DEBUG environment variable) flag is true.
+        """
+        from distutils.core import DEBUG
+        if DEBUG:
+            print msg
+
+
+    def finalize(self):
+        """ complete the files list by processing the given template """
+        if self.finalized:
+            return
+        if self.files == None:
+            self.files = []
+        if self.template != None:
+            if type(self.template) == StringType:
+                self.template = string.split(self.template,";")
+            filelist = FileList(self.warn,self.debug_print)
+            for line in self.template:
+                filelist.process_template_line(string.strip(line))
+            filelist.sort()
+            filelist.remove_duplicates()
+            self.files.extend(filelist.files)
+        self.finalized = 1
+
+# end class Data_Files
+
+###########################################################################
+# a more sophisticated install routine than distutils install_data
+
+class my_install_data (install_data):
+
+    def check_data(self,d):
+        """ check if data are in new format, if not create a suitable object.
+            returns finalized data object
+        """
+        if not isinstance(d, Data_Files):
+            self.warn(("old-style data files list found "
+                        "-- please convert to Data_Files instance"))
+            if type(d) is TupleType:
+                if len(d) != 2 or  not (type(d[1]) is ListType):
+                        raise DistutilsSetupError, \
+                          ("each element of 'data_files' option must be an "
+                            "Data File instance, a string or 2-tuple (string,[strings])")
+                d = Data_Files(copy_to=d[0],files=d[1])
+            else:
+                if not (type(d) is StringType):
+                        raise DistutilsSetupError, \
+                          ("each element of 'data_files' option must be an "
+                           "Data File instance, a string or 2-tuple (string,[strings])")
+                d = Data_Files(files=[d])
+        d.finalize()
+        return d
+
+    def run(self):
+        self.outfiles = []
+        install_cmd = self.get_finalized_command('install')
+
+        for d in self.data_files:
+            d = self.check_data(d)
+
+            install_dir = self.install_dir
+            # alternative base dir given => overwrite install_dir
+            if d.base_dir != None:
+                install_dir = getattr(install_cmd,d.base_dir)
+
+            # copy to an other directory
+            if d.copy_to != None:
+                if not os.path.isabs(d.copy_to):
+                    # relatiev path to install_dir
+                    dir = os.path.join(install_dir, d.copy_to)
+                elif install_cmd.root:
+                    # absolute path and alternative root set
+                    dir = change_root(self.root,d.copy_to)
+                else:
+                    # absolute path
+                    dir = d.copy_to
+            else:
+                # simply copy to install_dir
+                dir = install_dir
+                # warn if necceassary
+                self.warn("setup script did not provide a directory to copy files to "
+                          " -- installing right in '%s'" % install_dir)
+
+            dir=os.path.normpath(dir)
+            # create path
+            self.mkpath(dir)
+
+            # copy all files
+            for src in d.files:
+                if d.strip_dirs > 0:
+                    dst = string.join(string.split(src,os.sep)[d.strip_dirs:],os.sep)
+                else:
+                    dst = src
+                if d.preserve_path:
+                    # preserve path in filename
+                    self.mkpath(os.path.dirname(os.path.join(dir,dst)))
+                    out = self.copy_file(src, os.path.join(dir,dst))
+                else:
+                    out = self.copy_file(src, dir)
+                if type(out) is TupleType:
+                    out = out[0]
+                self.outfiles.append(out)
+
+        return self.outfiles
+
+    def get_inputs (self):
+        inputs = []
+        for d in self.data_files:
+            d = self.check_data(d)
+            inputs.append(d.files)
+        return inputs
+
+    def get_outputs (self):
+         return self.outfiles
+
+
+###########################################################################
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/simple_example.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# A very simple example that creates a random image from the
+# PseudoGimpy CAPTCHA, saves and shows it, and prints the list
+# of solutions. Normally you would call testSolutions rather
+# than reading this list yourself.
+#
+from Captcha.Visual.Tests import PseudoGimpy, AngryGimpy
+import numpy
+#from numpy import *
+
+#g = AngryGimpy()
+#i = g.render()
+#a = numpy.asarray(i)
+#b = numpy.zeros((2, 2), numpy.int8)
+#c = a == b
+#print c
+#i.save("output.png")
+#i.show()
+#print a
+#print g.solutions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/simple_example.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# A very simple example that creates a random image from the
+# PseudoGimpy CAPTCHA, saves and shows it, and prints the list
+# of solutions. Normally you would call testSolutions rather
+# than reading this list yourself.
+#
+from Captcha.Visual.Tests import PseudoGimpy, AngryGimpy
+import numpy
+#from numpy import *
+
+g = AngryGimpy()
+i = g.render()
+a = numpy.asarray(i)
+b = numpy.zeros((2, 2), numpy.int8)
+c = a == b
+print c
+i.save("output.png")
+i.show()
+#print a
+#print g.solutions
Binary file pycaptcha/test.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/transformations.py	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,25 @@
+
+import Numeric, Image
+    #""" Transforme une image PIL en objet numpy.array et vice versa"""
+
+
+def image2array(im):
+    #""" image vers array numpy"""
+    if im.mode not in ("L", "F"):
+        raise ValueError, "can only convert single-layer images"
+    if im.mode == "L":
+        a = Numeric.fromstring(im.tostring(), Numeric.UnsignedInt8)
+    else:
+        a = Numeric.fromstring(im.tostring(), Numeric.Float32)
+    a.shape = im.size[1], im.size[0]
+    return a
+
+def array2image(a):
+    #""" array numpy vers image"""
+    if a.typecode() == Numeric.UnsignedInt8:
+        mode = "L"
+    elif a.typecode() == Numeric.Float32:
+        mode = "F"
+    else:
+        raise ValueError, "unsupported image mode"
+    return Image.fromstring(mode, (a.shape[1], a.shape[0]), a.tostring())
Binary file pycaptcha/transformations.pyc has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycaptcha/transformations.py~	Thu Feb 11 05:09:46 2010 -0500
@@ -0,0 +1,25 @@
+
+import Numeric, Image
+    """ Transforme une image PIL en objet numpy.array et vice versa"""
+
+
+def image2array(im):
+    """ image vers array numpy"""
+    if im.mode not in ("L", "F"):
+        raise ValueError, "can only convert single-layer images"
+    if im.mode == "L":
+        a = Numeric.fromstring(im.tostring(), Numeric.UnsignedInt8)
+    else:
+        a = Numeric.fromstring(im.tostring(), Numeric.Float32)
+    a.shape = im.size[1], im.size[0]
+    return a
+
+def array2image(a):
+    """ array numpy vers image"""
+    if a.typecode() == Numeric.UnsignedInt8:
+        mode = "L"
+    elif a.typecode() == Numeric.Float32:
+        mode = "F"
+    else:
+        raise ValueError, "unsupported image mode"
+    return Image.fromstring(mode, (a.shape[1], a.shape[0]), a.tostring())