From 3210adefe8e5e53acfac6599b69783f7a164359c Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Thu, 21 Nov 2019 21:09:57 +0000 Subject: [PATCH] people: Render an avatar with letter if no avatar exists Signed-off-by: Michael Tremer --- src/web/people.py | 67 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/web/people.py b/src/web/people.py index 893a8063..1da6c275 100644 --- a/src/web/people.py +++ b/src/web/people.py @@ -1,9 +1,12 @@ #!/usr/bin/python +import PIL import datetime +import imghdr +import io import ldap import logging -import imghdr +import os.path import tornado.web import urllib.parse @@ -13,6 +16,9 @@ from . import auth from . import base from . import ui_modules +COLOUR_LIGHT = (237,232,232) +COLOUR_DARK = (49,53,60) + class IndexHandler(auth.CacheMixin, base.BaseHandler): @tornado.web.authenticated def get(self): @@ -56,7 +62,8 @@ class AvatarHandler(base.BaseHandler): if not avatar: logging.debug("No avatar uploaded for %s" % account) - return self.redirect(self.static_url("img/default-avatar.jpg")) + # Generate a random avatar with only one letter + avatar = self._get_avatar(account, size=size) # Guess content type type = imghdr.what(None, avatar) @@ -68,6 +75,62 @@ class AvatarHandler(base.BaseHandler): # Deliver payload self.finish(avatar) + def _get_avatar(self, account, size=None, **args): + letter = ("%s" % account)[0].upper() + + if size is None: + size = 256 + + # The generated avatar cannot be larger than 1024px + if size >= 2048: + size = 2048 + + # Cache key + key = "avatar:letter:%s:%s" % (letter, size) + + # Fetch avatar from the cache + avatar = self.memcached.get(key) + if not avatar: + avatar = self._make_avatar(letter, size=size, **args) + + # Cache for forever + self.memcached.set(key, avatar) + + return avatar + + def _make_avatar(self, letter, format="PNG", size=None, **args): + # Generate an image of the correct size + image = PIL.Image.new("RGBA", (size, size), COLOUR_LIGHT) + + # Have a canvas + draw = PIL.ImageDraw.Draw(image) + + # Load font + font = PIL.ImageFont.truetype(os.path.join( + self.application.settings.get("static_path", ""), + "fonts/Mukta-Bold.ttf" + ), size, encoding="unic") + + # Determine size of the printed letter + w, h = font.getsize(letter) + + # Mukta seems to be very broken and the height needs to be corrected + h //= 0.7 + + # Draw the letter in the center + draw.text(((size - w) / 2, (size - h) / 2), letter, + font=font, fill=COLOUR_DARK) + + with io.BytesIO() as f: + # If writing out the image does not work with optimization, + # we try to write it out without any optimization. + try: + image.save(f, format, optimize=True, **args) + except: + image.save(f, format, **args) + + return f.getvalue() + class CallsHandler(auth.CacheMixin, base.BaseHandler): @tornado.web.authenticated -- 2.47.3