terom@0: #!/usr/bin/python2.5 terom@3: import werkzeug terom@3: from werkzeug.exceptions import HTTPException terom@0: terom@1: from PIL import Image, ImageDraw, ImageFont, ImageEnhance terom@0: from cStringIO import StringIO terom@13: import random, itertools, time, os.path terom@0: terom@5: if not hasattr(itertools, 'izip_longest') : terom@5: terom@5: def izip_longest(*args, **kwds): terom@5: # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- terom@5: fillvalue = kwds.get('fillvalue') terom@5: def sentinel(counter = ([fillvalue]*(len(args)-1)).pop): terom@5: yield counter() # yields the fillvalue, or raises IndexError terom@5: fillers = itertools.repeat(fillvalue) terom@5: iters = [itertools.chain(it, sentinel(), fillers) for it in args] terom@5: try: terom@5: for tup in itertools.izip(*iters): terom@5: yield tup terom@5: except IndexError: terom@5: pass terom@5: terom@5: itertools.izip_longest = izip_longest terom@5: terom@3: class Defaults : terom@3: # settings terom@8: terom@8: text_lang = 'en' terom@0: terom@7: chars = [ u'"', u'!', u'?' ] terom@0: terom@8: colors = [ terom@3: "#0469af", terom@3: "#fbc614", terom@3: "#e1313b", terom@3: ] terom@3: terom@3: font_name = 'helvetica' terom@3: font_size = 30 terom@3: terom@8: bg_color = "#ffffff" terom@3: line_spacing = -10 terom@3: sharpness = 0.6 terom@0: terom@7: img_format = 'png' terom@7: terom@8: TEXT_BY_LANG = dict( terom@8: en = [ terom@8: u"aalto", terom@8: u"unive", terom@8: u"rsity" terom@8: ], terom@8: fi = [ terom@8: u"aalto", terom@8: u"yliop", terom@8: u"isto" terom@8: ], terom@8: se = [ terom@8: u"aalto", terom@8: u"univer", terom@8: u"sitetet", terom@8: ], terom@8: ) terom@8: terom@13: STATIC_PATH = "static" terom@13: terom@8: FONTS = { terom@3: 'dejavu-sans-bold': "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf", terom@13: 'helvetica': "fonts/HELR65W.TTF", terom@3: } terom@0: terom@8: IMAGE_FORMATS = { terom@8: 'jpeg': 'jpeg', terom@8: 'jpg': 'jpeg', terom@8: 'png': 'png', terom@8: 'bmp': 'bmp' terom@8: } terom@8: terom@6: FONT_SIZE_MAX = 1024 terom@13: IMG_SIZE_MAX = 1024 terom@13: terom@13: TILE_SIZE = (100, 100) terom@3: terom@3: # enable debugging terom@6: DEBUG = True terom@6: terom@0: terom@0: def randomize (seq) : terom@0: """ terom@0: Returns the given sequence in random order as a list terom@0: """ terom@0: terom@0: # copy terom@0: l = list(seq) terom@0: terom@0: # rearrange terom@0: random.shuffle(l) terom@0: terom@0: return l terom@0: terom@11: def randomize_str_char (str) : terom@11: """ terom@11: Randomize the given string by moving one char around terom@11: """ terom@11: terom@11: l = list(str) terom@11: terom@11: c = l.pop(random.randint(0, len(l) - 1)) terom@11: l.insert(random.randint(0, len(l)), c) terom@11: terom@11: return ''.join(l) terom@11: terom@11: def build_data (text, chars, line_colors, random_chars=True, random_text=False, random_text_char=False) : terom@0: """ terom@0: Returns a matrix of (text, color) tuples representing the data to render terom@0: terom@0: [ [ (str, str) ] ] terom@0: terom@0: text - list of lines terom@0: chars - list of random chars to interpse terom@0: line_colors - list of colors to draw the chars in terom@11: random_chars - randomize the lines the chars go in terom@11: random_text - randomize the chars in each line terom@11: random_text_char - randomize each line by moving one char around terom@0: """ terom@0: terom@0: data = [] terom@16: terom@16: # if no chars given, don't insert any terom@16: if not chars : terom@16: chars = [] terom@16: terom@16: # randomize char order across lines? terom@9: if random_chars : terom@9: chars = randomize(chars) terom@0: terom@5: for line, char, color in itertools.izip_longest(text, chars, line_colors, fillvalue=None) : terom@15: if not line : terom@15: continue terom@15: terom@0: # pick position to place char terom@16: if len(line) >= 2 : terom@16: pos = random.randint(1, len(line) - 1) terom@16: else : terom@16: pos = random.randint(0, 1) terom@16: terom@16: # default color terom@5: if not color : terom@5: color = "#000000" terom@16: terom@16: # randomize text in some way? terom@9: if random_text : terom@9: line = ''.join(randomize(line)) terom@11: terom@11: if random_text_char : terom@11: line = randomize_str_char(line) terom@0: terom@0: # split into three parts terom@5: if char : terom@5: data.append([ terom@5: (line[:pos], "#000000"), terom@5: (char, color), terom@5: (line[pos:], "#000000"), terom@5: ]) terom@5: else : terom@5: data.append([ terom@5: (line, "#000000"), terom@5: ]) terom@0: terom@0: return data terom@0: terom@0: def load_font (font_name, font_size) : terom@0: """ terom@0: Load a font by name terom@0: """ terom@0: terom@0: # load font terom@8: font_path = FONTS[font_name] terom@0: font = ImageFont.truetype(font_path, font_size) terom@0: terom@0: return font terom@0: terom@13: def render_img (data, font, background_color="#ffffff", line_spacing=0, img_size=None) : terom@0: """ terom@0: Render the data (as from build_data) as an image, using the given PIL.ImageFont, and return the PIL Image object terom@0: """ terom@0: terom@0: img_width = img_height = 0 terom@0: terom@0: img_data = [] terom@0: terom@13: # compute image/segment width/height terom@0: for segments in data : terom@0: line_width = line_height = 0 terom@0: terom@0: # build a new list of segments with additional info terom@0: line_segments = [] terom@0: terom@0: for seg_text, seg_color in segments : terom@0: # compute rendered text size terom@0: seg_width, seg_height = font.getsize(seg_text) terom@0: terom@0: # update line_* terom@0: line_width += seg_width terom@0: line_height = max(line_height, seg_height) terom@0: terom@0: # build the new segments list terom@0: line_segments.append((seg_text, seg_color, seg_width)) terom@0: terom@0: # update img_* terom@0: img_width = max(img_width, line_width) terom@0: img_height += line_height terom@0: img_data.append((line_segments, line_height)) terom@13: terom@13: if img_size : terom@13: # override size terom@13: img_width, img_height = img_size terom@0: terom@13: else : terom@13: # calculate height needed for line spacing terom@13: img_height += (len(img_data) - 1) * line_spacing terom@0: terom@0: # create image terom@0: img = Image.new("RGB", (img_width, img_height), background_color) terom@0: draw = ImageDraw.Draw(img) terom@0: terom@0: # draw text terom@0: img_y = 0 terom@0: for segments, line_height in img_data : terom@0: img_x = 0 terom@0: terom@0: # draw each segment build above, incremeing along img_x terom@0: for seg_text, seg_color, seg_width in segments : terom@0: draw.text((img_x, img_y), seg_text, font=font, fill=seg_color) terom@0: terom@0: img_x += seg_width terom@0: terom@0: img_y += line_height + line_spacing terom@0: terom@0: return img terom@0: terom@3: def effect_sharpness (img, factor) : terom@1: """ terom@3: Sharpen the image by the given factor terom@1: """ terom@1: terom@1: return ImageEnhance.Sharpness(img).enhance(factor) terom@1: terom@7: def build_img (img, format='png') : terom@0: """ terom@0: Write the given PIL.Image as a string, returning the raw binary data terom@7: terom@7: Format should be one of the PIL-supported image foarts terom@0: """ terom@0: terom@0: # render PNG output terom@0: buf = StringIO() terom@7: img.save(buf, format) terom@0: data = buf.getvalue() terom@0: terom@0: return data terom@0: terom@16: class OptionType (object) : terom@16: def parse (self, val) : terom@16: """ terom@16: Unicode value -> object terom@16: """ terom@3: terom@16: abstract terom@3: terom@16: def build (self, val) : terom@16: """ terom@16: object -> unicode value terom@16: """ terom@16: terom@16: return unicode(val) terom@16: terom@16: def input (self, val) : terom@16: """ terom@16: HTML input item terom@16: """ terom@16: terom@16: abstract terom@16: terom@16: class StringOption (OptionType) : terom@16: def parse (self, val) : terom@16: return unicode(val) terom@16: terom@16: def build (self, val) : terom@16: if val is None : terom@16: return "" terom@16: terom@16: else : terom@16: return val terom@16: terom@16: def input (self, opt, val) : terom@16: return """""" % dict( terom@16: name = opt.name, terom@16: value = self.build(val), terom@16: ) terom@16: terom@16: def select (self, opt, val) : terom@16: return """""" % dict( terom@16: name = opt.name, terom@16: options = '\n'.join( terom@16: "\t" % dict( terom@16: value = self.build(optval), terom@16: selected = 'selected="selected"' if val == optval else "", terom@16: ) for optval in opt.range terom@16: ), terom@16: ) terom@16: terom@16: class BoolOption (OptionType) : terom@16: def parse (self, val) : terom@16: if val.lower() in ('true', 't', '1', 'yes', 'y') : terom@16: return True terom@16: terom@16: elif val.lower() in ('false', 'f', '0', 'no', 'n') : terom@16: return False terom@16: terom@16: else : terom@16: raise ValueError(val) terom@16: terom@16: def input (self, opt, val) : terom@16: return """""" % dict( terom@16: name = opt.name, terom@16: checked = 'checked="checked"' if val else '', terom@16: ) terom@16: terom@16: class IntOption (StringOption) : terom@16: def parse (self, val) : terom@16: return int(val) terom@16: terom@16: class FloatOption (StringOption) : terom@16: def parse (self, val) : terom@16: return float(val) terom@16: terom@16: class ColorOption (StringOption) : terom@16: def _parse (self, val) : terom@16: if val.startswith('#') : terom@16: int(val[1:], 16) terom@16: terom@16: return val terom@16: else : terom@16: raise ValueError(val) terom@16: terom@3: terom@8: class Option (object) : terom@8: def __init__ (self, name, is_list, type, default, range) : terom@8: self.name = name terom@8: self.is_list = is_list terom@8: self.type = type terom@8: self.default = default terom@8: self.range = range terom@3: terom@16: def parse (self, args, force_bool=False) : terom@8: if self.is_list : terom@8: if self.name in args : terom@16: l = args.getlist(self.name, self.type.parse) terom@16: terom@16: # special-case to handle a single param with a newline-separtated list terom@16: if len(l) == 1 : terom@16: if not l[0] : terom@16: return None terom@16: terom@16: else : terom@16: return l[0].split('\r\n') terom@16: terom@16: else : terom@16: return l terom@16: terom@8: else : terom@8: return self.default terom@16: terom@8: else : terom@16: if isinstance(self.type, BoolOption) and force_bool : terom@16: return self.name in args terom@16: terom@16: elif isinstance(self.type, BoolOption) and not self.default and self.name in args : terom@10: return True terom@10: terom@10: else : terom@16: return args.get(self.name, self.default, self.type.parse) terom@16: terom@16: def build_list (self, value) : terom@16: if self.is_list and value : terom@16: return [self.type.build(val) for val in value] terom@16: terom@16: else : terom@16: return [self.type.build(value)] terom@16: terom@16: def _build_input (self, value) : terom@16: if self.is_list : terom@16: return """\ terom@16: """ % dict( terom@16: name = self.name, terom@16: data = '\n'.join(self.type.build(val) for val in value) if value else '', terom@16: ) terom@16: terom@16: elif self.range : terom@16: return self.type.select(self, value) terom@16: terom@16: else : terom@16: return self.type.input(self, value) terom@16: terom@16: def build_form (self, opts) : terom@16: value = opts[self.name] terom@16: terom@16: return """\ terom@16:
Aaltologotin pulauttaa sulle uuden, sattumanvaraisesti valitun aalto-logon!
terom@16: terom@16: terom@16: