This project aims to build business analytical cards images to be used embedded or ubiquitous on no convetional applications like: chatbots, messenger, whatsapp, smartwatches and etc. It’s a technique worth discussing on its own. After all, "they say that a picture is worth 1000 words. So that means a picture that actually contains words must be worth way more".
In this article I will demonstrate how create a python card renderer that: 1) load a background image, 2) overlay that image with some texts with customized fonts and colors and 3) save the result as final image. After, I will show how to build a server with this renderer to create an API that can be used to download dynamic analytical business cards. This cards than can be embedded in other applications like: chatbots or live monitors. I am using this approach in some experiments at bizubox, a startup that I'm a co-founder, trying to bring analytical information in a more user friendly way to our customers.
Let’s say you had to sent sales information daily to a client, and you wanted a way to show that with a bit more flair than a excel dashboard, then you have the idea to send some analytical cards using messenger or whatsapp. The concept and the design I developed using sketch, a vectorized design toolkit, the following picture shows this idea as a business card. The variables (headerline, subline, bolderline, balanceline) will be dynamically rendered using python, let's go to code.
For this project I will use pillow, a python image manipulation library, it can be installed with pip. And I will use the flask server to create the API.
sudo pip install pillow
sudo pip install flask
And than import the image lib
from PIL import Image, ImageDraw, ImageFont
Ok, now I’ll be loading two external resources: a TrueType font for our text, a background image and png overlay triangle icon. Now just declare a function to import the fonts (the density variable is as multiplier to increase the pixel per inch):
def get_fonts(density):
# Define fonts for regular text and heading
header_font = ImageFont.truetype("assets/fonts/rajdhani-semibold.ttf", 28*density)
subline_font = ImageFont.truetype("assets/fonts/rajdhani-medium.ttf", 14*density)
bolderline_font = ImageFont.truetype("assets/fonts/rajdhani-bold.ttf", 11*density)
footer_font = ImageFont.truetype("assets/fonts/rajdhani-semibold.ttf", 18*density)
return header_font, subline_font, bolderline_font, footer_font
Going on, I will declare a card render function to put together a very visually appealing final product with minimal effort and code, of course there’s a whole another ways to explore fonts, overlays and many awesome features out there. The first part of the code will add the background degrade image and will add the triangle green icon to that image. Finally, a surface will add the dynamic texts that I used as the input of the render function:
The intermediary image with background and icon overlay:
The code of the render function:
def card_render(header='', subline='', bolderline='', balanceline='', density=3):
# Define fonts for regular text and heading
header_font, subline_font, bolderline_font, footer_font = get_fonts(density)
# Load background image
img = Image.open("assets/imgs/card_background@3x.png")
# Added triangle icon overlay
icon_overlay = Image.open("assets/imgs/triangle_icon.png")
img.paste(icon_overlay, (93*density, 89*density), icon_overlay)
# Build a surface
surface = ImageDraw.Draw(img)
# Write heading
surface.text((47*density, 11*density), header, font=header_font)
surface.text((70*density, 46*density), subline, font=subline_font)
surface.text((97*density, 65*density), bolderline, font=bolderline_font)
surface.text((103*density, 80*density), balanceline, font=footer_font, fill='#A9EAB0')
return img
def save_img():
img = card_render(
header="$ 12,041.81",
subline="sales yesterday",
bolderline="monday",
balanceline="19%",
density=3
)
img.save(open("rendered/temp_card.png", "wb"), "PNG")
The final rendered image:
I'm using flask for this application. I'd like to serve a API to dynamically render the card (generated by PIL), and send this card image to client without saving on disk. The following code can accomplish that:
import io
import renderer as ren
from flask import Flask, send_file
app = Flask(__name__)
# Send an image (dynamically generated by pillow) to client without saving on disk.
def serve_pil_image(pil_img):
img_io = io.BytesIO()
pil_img = pil_img.convert('RGB')
pil_img.save(img_io, 'JPEG', quality=100)
img_io.seek(0)
return send_file(img_io, mimetype='image/jpeg')
# Flask controller to download de image
@app.route('/cards/sales_card.jpeg')
def serve_img():
img = ren.card_render(
header="$ 12,041.81",
subline="sales yesterday",
bolderline="monday",
balanceline="19%",
density=3
)
return serve_pil_image(img)
Now just call '''export FLASK_APP=server.py && python -m flask run''' to start the flask app and go to the browser(http://127.0.0.1:5000/cards/sales_card.jpeg) to see the result:
[Flask] (http://flask.pocoo.org/) [Pillow] (https://pillow.readthedocs.io/en/5.3.x/)