Overlay text on images with PHP

#programming #php

Some time ago, someone asked me how to create a list of diplomas using a static background image and a list of names. The problem was simple enough to be implemented in less than 30 mins, so I decided to give a try and here is what I ended up with.

This is a simple PHP script with some assumptions that can be run directly in the command line. It can easily be tweaked to fulfill other more complex requirements, like water marking images, mobile optimized images, reading data from Google Spreadsheets or Airtable, etc., so feel free to fork the repo or download the source and hack it as you need it.

With the code below I go:

From this… …to this
Empty Diploma Filled in Diploma

To the code.


First, some variables so we can tweak the script behavior without having to figure out where in the code are this things setup

$csv_file = 'names.csv';
$background = 'background.jpg';
$signature = 'sig.png';
$font = 'ConeriaScript.ttf';

Then, we read the CSV file and parse so it is easier to read later by converting it to an associative array with the column names as the keys.

// CSV code from
$csv = array_map('str_getcsv', file($csv_file));
array_walk($csv, function (&$a) use ($csv) {
    $a = array_combine($csv[0], $a);

For this specific background we will need the current date as three strings to be placed in three different places.

$day = date('j');
$month = date('F');
$year = date('Y');

Now we start having fun with PHP’s image functions. First, we need to read our background image to get some basic info and do some preparation, like setting the text color, the image width (for centering the text), and reading the signature image and its sizes.

$image = imagecreatefromjpeg($background);
$color = imagecolorallocate($image, 0, 0, 0);
$width = imagesx($image);
$signature = imagecreatefrompng($signature);
$signature_width = imagesx($signature);
$signature_height = imagesy($signature);

Now that we have the image data ready, we will start reading each name in our list so we can generate individual images with all the overlaid text. For easier reading, I am saving the name and reason in their own variables.

  foreach ($csv as $row) {
    $name = $row['Name'];
    $reason = $row['Reason'];

Here, I am reading the background image again. For the first item in the loop this does not make sense because I already read it above, but since all the image* functions alter their input, if I do not do this I will end every image in the list will end up all the previous text from all the previous images overlapped; not good. I am also getting the text “bound boxes” which are basically the positions of the four corners of my text; I do this to be able to center the text in the image.

    $image = imagecreatefromjpeg($background);
    $name_box = imagettfbbox(40, 0, $font, $name);
    $reason_box = imagettfbbox(40, 0, $font, $reason);

Now it is time to actually render the text on the image. This is done by providing some information to PHP, including the destination image, text size, angle, x and y position, color, font (yes you can use your fonts!), and last but not least, the actual text.

    imagettftext($image, 40, 0, ($width - $name_box[2]) / 2, 635, $color, $font, $name);
    imagettftext($image, 40, 0, ($width - $reason_box[2]) / 2, 790, $color, $font, $reason);
    imagettftext($image, 32, 0, 400, 895, $color, $font, $day);
    imagettftext($image, 32, 0, 600, 895, $color, $font, $month);
    imagettftext($image, 32, 0, 600, 975, $color, $font, $year);

Once the text is overlaid correctly, I need to add the “signature” image on top of the text so it looks nicer, so I tell PHP to copy the signature image (a transparent PNG file) on top, as you can see, I had to not only provide position, but also size.

    imagecopy($image, $signature, 400, 980, 0, 0, $signature_width, $signature_height);

Almost there! Here I am saving the resulting image to disk by providing a name. I I did not provide the name, PHP would just output the content, which could be used instead as a download link by also providing the correct header.

    imagejpeg($image, "diplomas/$name.jpg");

I do not want to clutter my memory, so once I am done with the image, it is destroyed.


And done, to the next item on the list!


As you can see this is a fairly simple yet powerful script that can be used for many different purposes. Feel free to see or fork the full project and these are the links to the image functions used in this example:

I hope you enjoy this post and find it useful!