87
|
1 """ Captcha.Visual.Text
|
|
2
|
|
3 Text generation for visual CAPTCHAs.
|
|
4 """
|
|
5 #
|
|
6 # PyCAPTCHA Package
|
|
7 # Copyright (C) 2004 Micah Dowty <micah@navi.cx>
|
|
8 #
|
|
9
|
|
10 import random, os
|
|
11 from Captcha import Visual, File
|
|
12 import ImageFont, ImageDraw
|
|
13
|
|
14
|
|
15 class FontFactory(File.RandomFileFactory):
|
|
16 """Picks random fonts and/or sizes from a given list.
|
|
17 'sizes' can be a single size or a (min,max) tuple.
|
|
18 If any of the given files are directories, all *.ttf found
|
|
19 in that directory will be added.
|
|
20 """
|
|
21 extensions = [".ttf"]
|
|
22 basePath = "fonts"
|
|
23
|
|
24 # arguments variables a modifier pour mettre le chemin vers les fontes.
|
|
25 def __init__(self, sizes, *fileNames):
|
|
26 File.RandomFileFactory.__init__(self, *fileNames)
|
|
27
|
|
28 if type(sizes) is tuple:
|
|
29 self.minSize = sizes[0]
|
|
30 self.maxSize = sizes[1]
|
|
31 else:
|
|
32 self.minSize = sizes
|
|
33 self.maxSize = sizes
|
|
34
|
|
35 def pick(self):
|
|
36 """Returns a (fileName, size) tuple that can be passed to ImageFont.truetype()"""
|
|
37 fileName = File.RandomFileFactory.pick(self)
|
|
38 size = int(random.uniform(self.minSize, self.maxSize) + 0.5)
|
|
39 return (fileName, size)
|
|
40
|
|
41 # Predefined font factories
|
|
42 defaultFontFactory = FontFactory(25, "vera", "others")
|
|
43 #defaultFontFactory = FontFactory((30, 40), "vera")
|
|
44
|
|
45 class TextLayer(Visual.Layer):
|
|
46 """Represents a piece of text rendered within the image.
|
|
47 Alignment is given such that (0,0) places the text in the
|
|
48 top-left corner and (1,1) places it in the bottom-left.
|
|
49
|
|
50 The font and alignment are optional, if not specified one is
|
|
51 chosen randomly. If no font factory is specified, the default is used.
|
|
52 """
|
|
53 def __init__(self, text,
|
|
54 alignment = None,
|
|
55 font = None,
|
|
56 fontFactory = None,
|
|
57 textColor = "white",
|
|
58 borderSize = 0,
|
|
59 borderColor = None,
|
|
60 ):
|
|
61 if fontFactory is None:
|
|
62 global defaultFontFactory
|
|
63 fontFactory = defaultFontFactory
|
|
64
|
|
65 if font is None:
|
|
66 font = fontFactory.pick()
|
|
67
|
|
68 if alignment is None:
|
|
69 alignment = (random.uniform(0,1),
|
|
70 random.uniform(0,1))
|
|
71
|
|
72 self.text = text
|
|
73 self.alignment = alignment
|
|
74 self.font = font
|
|
75 self.textColor = textColor
|
|
76 self.borderSize = borderSize
|
|
77 self.borderColor = borderColor
|
|
78
|
|
79 def render(self, img):
|
|
80 font = ImageFont.truetype(*self.font)
|
|
81 textSize = font.getsize(self.text)
|
|
82 draw = ImageDraw.Draw(img)
|
|
83
|
|
84 # Find the text's origin given our alignment and current image size
|
|
85 x = int((img.size[0] - textSize[0] - self.borderSize*2) * self.alignment[0] + 0.5)
|
|
86 y = int((img.size[1] - textSize[1] - self.borderSize*2) * self.alignment[1] + 0.5)
|
|
87
|
|
88 # Draw the border if we need one. This is slow and ugly, but there doesn't
|
|
89 # seem to be a better way with PIL.
|
|
90 if self.borderSize > 0:
|
|
91 for bx in (-1,0,1):
|
|
92 for by in (-1,0,1):
|
|
93 if bx and by:
|
|
94 draw.text((x + bx * self.borderSize,
|
|
95 y + by * self.borderSize),
|
|
96 self.text, font=font, fill=self.borderColor)
|
|
97
|
|
98 # And the text itself...
|
|
99 draw.text((x,y), self.text, font=font, fill=self.textColor)
|
|
100
|
|
101 ### The End ###
|