|
1 #!/usr/bin/python2.5 |
|
2 import cgi, cgitb |
|
3 |
|
4 cgitb.enable() |
|
5 |
|
6 from PIL import Image, ImageDraw, ImageFont, ImageEnhance |
|
7 from cStringIO import StringIO |
|
8 import random |
|
9 |
|
10 |
|
11 # settings |
|
12 text = [ |
|
13 "aalto", |
|
14 "unive", |
|
15 "rsity" |
|
16 ] |
|
17 |
|
18 random_chars = [ '"', '!', '?' ] |
|
19 |
|
20 line_colors = [ |
|
21 "#0469af", |
|
22 "#fbc614", |
|
23 "#e1313b", |
|
24 ] |
|
25 |
|
26 fonts = { |
|
27 'dejavu-sans-bold': "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf", |
|
28 'helvetica': "HELR65W.TTF", |
|
29 } |
|
30 |
|
31 font_name = 'helvetica' |
|
32 font_size = 30 |
|
33 |
|
34 def randomize (seq) : |
|
35 """ |
|
36 Returns the given sequence in random order as a list |
|
37 """ |
|
38 |
|
39 # copy |
|
40 l = list(seq) |
|
41 |
|
42 # rearrange |
|
43 random.shuffle(l) |
|
44 |
|
45 return l |
|
46 |
|
47 def build_data (text, chars, line_colors) : |
|
48 """ |
|
49 Returns a matrix of (text, color) tuples representing the data to render |
|
50 |
|
51 [ [ (str, str) ] ] |
|
52 |
|
53 text - list of lines |
|
54 chars - list of random chars to interpse |
|
55 line_colors - list of colors to draw the chars in |
|
56 """ |
|
57 |
|
58 data = [] |
|
59 |
|
60 for line, char, color in zip(text, chars, line_colors) : |
|
61 # pick position to place char |
|
62 pos = random.randint(1, len(line) - 1) |
|
63 |
|
64 # split into three parts |
|
65 data.append([ |
|
66 (line[:pos], "#000000"), |
|
67 (char, color), |
|
68 (line[pos:], "#000000"), |
|
69 ]) |
|
70 |
|
71 return data |
|
72 |
|
73 def load_font (font_name, font_size) : |
|
74 """ |
|
75 Load a font by name |
|
76 """ |
|
77 |
|
78 # load font |
|
79 font_path = fonts[font_name] |
|
80 font = ImageFont.truetype(font_path, font_size) |
|
81 |
|
82 return font |
|
83 |
|
84 def render_img (data, font, background_color="#ffffff", line_spacing=0) : |
|
85 """ |
|
86 Render the data (as from build_data) as an image, using the given PIL.ImageFont, and return the PIL Image object |
|
87 """ |
|
88 |
|
89 img_width = img_height = 0 |
|
90 |
|
91 img_data = [] |
|
92 |
|
93 # compute image width/height |
|
94 for segments in data : |
|
95 line_width = line_height = 0 |
|
96 |
|
97 # build a new list of segments with additional info |
|
98 line_segments = [] |
|
99 |
|
100 for seg_text, seg_color in segments : |
|
101 # compute rendered text size |
|
102 seg_width, seg_height = font.getsize(seg_text) |
|
103 |
|
104 # update line_* |
|
105 line_width += seg_width |
|
106 line_height = max(line_height, seg_height) |
|
107 |
|
108 # build the new segments list |
|
109 line_segments.append((seg_text, seg_color, seg_width)) |
|
110 |
|
111 # update img_* |
|
112 img_width = max(img_width, line_width) |
|
113 img_height += line_height |
|
114 img_data.append((line_segments, line_height)) |
|
115 |
|
116 # calculate height needed for line spacing |
|
117 img_height += (len(img_data) - 1) * line_spacing |
|
118 |
|
119 # create image |
|
120 img = Image.new("RGB", (img_width, img_height), background_color) |
|
121 draw = ImageDraw.Draw(img) |
|
122 |
|
123 # draw text |
|
124 img_y = 0 |
|
125 for segments, line_height in img_data : |
|
126 img_x = 0 |
|
127 |
|
128 # draw each segment build above, incremeing along img_x |
|
129 for seg_text, seg_color, seg_width in segments : |
|
130 draw.text((img_x, img_y), seg_text, font=font, fill=seg_color) |
|
131 |
|
132 img_x += seg_width |
|
133 |
|
134 img_y += line_height + line_spacing |
|
135 |
|
136 return img |
|
137 |
|
138 def effect_smooth (img, factor) : |
|
139 """ |
|
140 De-sharpen the image by the given factor |
|
141 """ |
|
142 |
|
143 return ImageEnhance.Sharpness(img).enhance(factor) |
|
144 |
|
145 def build_png (img) : |
|
146 """ |
|
147 Write the given PIL.Image as a string, returning the raw binary data |
|
148 """ |
|
149 |
|
150 # render PNG output |
|
151 buf = StringIO() |
|
152 img.save(buf, "png") |
|
153 data = buf.getvalue() |
|
154 |
|
155 return data |
|
156 |
|
157 def response (type, data) : |
|
158 """ |
|
159 Write out the HTTP Response |
|
160 """ |
|
161 |
|
162 from sys import stdout |
|
163 |
|
164 stdout.write("Content-Type: %s\r\n" % type) |
|
165 stdout.write("Content-Length: %d\r\n" % len(data)) |
|
166 stdout.write("\r\n") |
|
167 stdout.write(data) |
|
168 |
|
169 def main () : |
|
170 data = build_data(text, randomize(random_chars), line_colors) |
|
171 font = load_font(font_name, font_size) |
|
172 |
|
173 img = render_img(data, font) |
|
174 img = effect_smooth(img, 0.6) |
|
175 png_data = build_png(img) |
|
176 |
|
177 response("image/png", png_data) |
|
178 |
|
179 if __name__ == '__main__' : |
|
180 main() |
|
181 |