Mercurial > ift6266
changeset 87:4775b4195b4b
code pour la generation de captchas
line wrap: on
line diff
--- /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.
--- /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 ###
--- /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 ###
--- /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 ###
--- /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 ###
--- /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 ###
--- /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 ###
--- /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/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 ###
--- /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 ###
--- /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 ###
--- /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 ###
--- /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.
--- /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>
--- /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
--- /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)
--- /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 ###
--- /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
--- /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())
--- /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())