Ashley Sheridan​.co.uk

Imogen – An Open Source Image Generator For Emails

Posted on

A couple of years ago I was working closely with some email developers to develop a system that could generate images on the fly. This was born of the need for emails to have content that could change after the moment it was sent, and be fresh at the point a recipient opened it.

The end result of this system was a rudimentary system written in PHP that could generate such images, and it was used for a couple of email campaigns. Unfortunately, it was only used for a couple of campaigns, and the source code rights didn’t belong to me, so it wasn’t something I could use after leaving the place of work I initially developed it at.

Why Open Source

The one key advantage the old image generator (affectionately named Genie) had over others that I had researched at the time was that it could also produce animated GIFs with dynamic content, and I still feel that to be a feature which has enormous potential and value.

As I’m in no position to release the source code of Genie, the only solution is to re-write the entire thing from scratch under an open source license, so that others can benefit.

The Platform

Like Genie, I am building Imogen on the Laravel PHP framework, as it’s fast, powerful, and I’m most familiar with it, but I took the opportunity to use the 5.4 version of the framework instead of the 4.2 that Genie was built with. I’m still using GD for the image creation and manipulation work, as it’s something I’ve been using with PHP for over a decade and it does the job perfectly.

Building the System

The heart of Imogen are the image layer generators, which operate a little bit like Photoshop image layers. Each layer generator adds to the overall image, building upon what went before it. Different generator classes create different types of layer, ranging from adding text, right through to adding Google maps with dynamic markers.

Layers of an image that build up a final image
Diagram showing 3 layers (text, sub-image, and base image) of an image as built by Imogen and the final image of all merged layers.

Each layer combines into a whole image, which is then served up as if it were a regular image that already existed on the web server.

Around this core is the caching system. Once an image has been generated, a local copy is saved into a directory on the server. If the same image is requested later by another request (for example, if two email recipients both lived in the same area and received an email containing a Google map centred on their postcode) then the version which exists in this local cache directory would be served up instead, bypassing the all the GD code which creates the image. This is crucial, as creating images will always be slower than serving up one that already exists. By removing some time consuming steps for some users, the server load is greatly reduced.

Configuration

If the generators are the body of the system, and the caching are the arms and the legs, the head would be the config for each image. Each image has its own config file, which contains all the information required, from the font(s) to use for each text layer, the size and rotation of external images added to the base, and the duration each image should be cached for. As these files are just Laravel config files, technically they do allow for PHP code to be included. This is something I did do previously with Genie, but is something I want to avoid with Imogen, as it’s not a clean thing to do code-wise.

Here’s a sample config that places a Google map on a green background, centered and with a marker on a supplied postcode (passed in as a query string parameter), with two extra markers placed at other locations in London (it helps for the test to use a postcode near those two locations otherwise the map won’t show them!)

<?php return array( 'type' => 'flat', 'description' => '', 'format' => 'png', 'width' => 640, 'height' => 480, 'background' => '#00b070', 'cache' => '19m 30s', 'overlays' => array( 'map' => array( 'type' => 'googlemap', 'api_key' => array( 'google api key', ), 'center' => '{{postcode}}', 'zoom' => 12, 'source_width' => 640, 'source_height' => 480, 'markers' => array( '{{postcode}}', 'British Museum, London, UK', 'Victoria and Albert Museum, London, UK' ), 'markers_colour' => '#0070b0', 'x' => 160, 'y' => 120, 'dest_width' => 320, 'dest_height' => 240, ), ), );

Features

Currently, as this is a re-write from scratch, it’s lacking features that Genie had. The main reason for this is simple: this is a re-write, it’s not using the same code copy-pasted across from one codebase to the other. The only thing that remains the same is the overarching idea: to produce dynamic images upon request. As I continue to work on this (it’s a labour of love, so it only sees attention during my rare free time at weekends and evenings) I’ll keep the repositories readme.md file up to date with the feature set and documentation. The killer feature that Genie had, dynamic animated GIFs, is not (at time of writing) present within Imogen.

The Source Code

If you want to check out the project, create a fork, or contribute some code/documentation towards the project, feel free to check out the source code on GitHub