import logging import os import re import textwrap from PIL import Image, ImageDraw, ImageFont def scale_box(inbox, imgsize): (i1x, i1y, i2x, i2y) = inbox (w, h) = imgsize return (i1x*w, i1y*h, i2x*w, i2y*h) def max_size(draw, inbox, text, font, size): anchor='lt' if '\n' in text: anchor=None f = ImageFont.truetype(font, size) (i1x, i1y, i2x, i2y) = inbox (b1x, b1y, b2x, b2y) = draw.textbbox((i1x, i1y), text, font=f, anchor=anchor) if (b1x < i1x or b1y < i1y or b2x > i2x or b2y > i2y): return max_size(draw, inbox, text, font, size-5) return size def fix_outliers(text): modifiers = [('[ÖQ]', 'O'), ('[ÅÄ]', 'A'), ('[yqpg]', 'u'), ('j', 'i')] for (pat, rep) in modifiers: text = re.sub(pat, rep, text) return text class Thumbnailer: def __init__(self, config, ldap): self.base = config['base'] self.fill = config['textcolor'] self.ldap = ldap self.logger = logging.getLogger('play-daemon') self.thumbname = 'thumb.png' self.font = 'verdana.ttf' self.titlebox = (1/8, 0.4, 7/8, 0.5) self.descrbox = (1/8, 0.55, 7/8, 0.8) def create_if_missing(self, package): if not package['thumb']: title = package['title']['sv'] description = package['description'] courses = ', '.join(package['courses']) presenters = ', '.join([self.ldap.get_name(uid) for uid in package['presenters']]) extras = [] if courses: extras.append(courses) if presenters: extras.append(presenters) extra = ': '.join(extras) workbase = package['workbase'] pres_id = package['id'] self.logger.debug('%s - Creating thumbnail', pres_id) self.logger.debug('%s - Title: %s', pres_id, title) self.logger.debug('%s - Description: %s', pres_id, description) self.logger.debug('%s - Extra: %s', pres_id, extra) package['thumb'] = self.make_thumb(workbase, title, description, extra) self.logger.info('%s - Thumbnail created', pres_id) return package def make_thumb(self, workbase, title, description, extra): with Image.open(self.base) as im: draw = ImageDraw.Draw(im) # Size and draw title s_titlebox = scale_box(self.titlebox, im.size) (t1x, t1y, t2x, t2y) = s_titlebox title_size = max_size(draw, s_titlebox, fix_outliers(title), self.font, 140) draw.text((t1x, t2y), title, fill=self.fill, anchor='ls', font=ImageFont.truetype(self.font, title_size)) # Size and draw description s_descrbox = scale_box(self.descrbox, im.size) (d1x, d1y, d2x, d2y) = s_descrbox descr = textwrap.fill(description) descr_size = max_size(draw, s_descrbox, descr, self.font, 75) f = ImageFont.truetype(self.font, descr_size) draw.multiline_text((d1x, d1y), descr, fill=self.fill, font=f) # Size and draw extra if extra: # Get effective size of the description text (b1x, b1y, b2x, b2y) = draw.textbbox((d1x, d1y), descr, font=f) # Create a preliminary bounding box for extra extrabox = (d1x, b2y, d2x, im.height) # Make sure the text fits in the box extra_size = max_size(draw, extrabox, extra, self.font, 50) # Offset the extra box by one line height extrabox = (extrabox[0], extrabox[1] + extra_size, extrabox[2], extrabox[3]) (p1x, p1y, p2x, p2y) = extrabox f = ImageFont.truetype(self.font, extra_size) # Draw it draw.text((p1x, p1y), extra, fill=self.fill, font=f, anchor='lt') im.save(os.path.join(workbase, self.thumbname)) return self.thumbname