User Tools

Site Tools


wiki:software:code:php:gdimages

PHP: Dynamically generating images from text

This article was originally published on December 14, 2008.

If you’ve visited my site more than once, you might start to notice that the silly little tagline next to the logo in my header changes, in both content and appearance.

This is accomplished by way of a fancy little bit of PHP. The image generation uses the GD library (with FreeType support), and the fonts are all TrueType.

Again, since my Linode didn’t come with anything preinstalled, I needed to install GD first. It seemed like it might be a bit of a pain – the PHP manual page indicates I was going to have to recompile PHP. Fortunately, this was not the case.

I just had to download it from Ubuntu’s package manager…

apt-get install php5-gd

…and add it to the “extensions” section of php.ini…

nano /etc/php5/apache2/php.ini
 
extension=gd.so

…and restart Apache.

apache2ctl -k graceful

Once that’s out of the way, creating the image was remarkably simple – especially since I found a ready-made script that did exactly what I wanted.

The code is pretty long, so hit the jump to see the rest.

Here’s the actual script, broken down into sections:

First, we tell the browser that this page is a PNG image. This line needs to come before any other code, or else you might start seeing errors.

Next, we create a class with all the values we want to use for our image, except for the font and text.

$transparent tells GD to create a transparent image, so that it can be used against any background. The first set of RGB values indicate the color of the text, and the second set is for the background color, in order to anti-alias the text. I selected a shade of green in the same area of my header gradient where the text appears, and it blends in pretty smoothly.

<?php
  Header("Content-type: image/png");
 
  class textPNG {
    var $size = 20;
    var $rot = 0; // rotation in degrees
    var $pad = 0; // padding
    var $transparent = 1; // transparency set to on
    var $red = 255; // text colors
    var $grn = 255;
    var $blu = 255;
    var $bg_red = 110; // background colors
    var $bg_grn = 218;
    var $bg_blu = 110;

Next, we write the function that will actually create the image, starting out with some empty variables. The two variables that are passed to the function ($foo and $font) are going to give us the text to display, and the font file to use.

    function draw($foo, $font) {
      $width = 0;
      $height = 0;
      $offset_x = 0;
      $offset_y = 0;
      $bounds = array();
      $image = "";

Now we need to figure out how big the image should be. First, we determine the height of the text based on a capital “W”. This will work for most fonts, but some of the handwriting ones I use in my tagline image have unusually tall characters, so I ended up manually specifying the height (e.g. $font-height = 55;).

      // determine font height.
      $bounds = ImageTTFBBox($this->size, $this->rot, $font, "W");
      if ($this->rot < 0) {
        $font_height = abs($bounds[7]-$bounds[1]);
      } else if ($this->rot > 0) {
        $font_height = abs($bounds[1]-$bounds[7]);
      } else {
        $font_height = abs($bounds[7]-$bounds[1]);
      }

Then we determine the width and the vertical and horizontal coordinates for the bottom left corner of the text. I ended up setting the $height variable to a specific value.

      // determine bounding box.
      $bounds = ImageTTFBBox($this->size, $this->rot, $font, $foo);
      if ($this->rot < 0) {
        $width = abs($bounds[4]-$bounds[0]);
        $height = abs($bounds[3]-$bounds[7]);
        $offset_y = $font_height;
        $offset_x = 0;
 
      } else if ($this->rot > 0) {
        $width = abs($bounds[2]-$bounds[6]);
        $height = abs($bounds[1]-$bounds[5]);
        $offset_y = abs($bounds[7]-$bounds[5])+$font_height;
        $offset_x = abs($bounds[0]-$bounds[6]);
 
      } else {
        $width = abs($bounds[4]-$bounds[6]);
        $height = abs($bounds[7]-$bounds[1]);
        $offset_y = $font_height;
        $offset_x = 0;
      }

The last bit of our function generates the actual image, using the imagettftext function of the GD library.

      $image = imagecreate($width+($this->pad*2)+1,$height+($this->pad*2)+1);
 
      $background = ImageColorAllocate($image, $this->bg_red, $this->bg_grn, $this->bg_blu);
      $foreground = ImageColorAllocate($image, $this->red, $this->grn, $this->blu);
 
      if ($this->transparent) ImageColorTransparent($image, $background);
      ImageInterlace($image, false);
 
      // render it.
      ImageTTFText($image, $this->size, $this->rot, $offset_x+$this->pad, $offset_y+$this->pad, $foreground, $font, $foo);
 
      // output PNG object.
      imagePNG($image);
    }
  }

Now we set variables based on the values specified in the original class we created.

  $text = new textPNG;
 
  if (isset($msg)) $text->msg = $msg; // text to display
  if (isset($font)) $text->font = $font; // font to use (include directory if needed).
  if (isset($size)) $text->size = $size; // size in points
  if (isset($rot)) $text->rot = $rot; // rotation
  if (isset($pad)) $text->pad = $pad; // padding in pixels around text.
  if (isset($red)) $text->red = $red; // text color
  if (isset($grn)) $text->grn = $grn; // ..
  if (isset($blu)) $text->blu = $blu; // ..
  if (isset($bg_red)) $text->bg_red = $bg_red; // background color.
  if (isset($bg_grn)) $text->bg_grn = $bg_grn; // ..
  if (isset($bg_blu)) $text->bg_blu = $bg_blu; // ..
  if (isset($tr)) $text->transparent = $tr; // transparency flag (boolean).

These next two sections are specifically for my application, with a random tagline and font file. First, we read the text file and create an array, using each line as an array element. Then, we select a random element from the array and set it to a variable. The text file is stored in the same directory as the PHP page.

  //get a random tagline from the text file
  $fname = file_get_contents('tags.txt');
  $tagarray = explode("n", $fname);
  $tagnum = count($tagarray)-1;
  $line = $tagarray[rand(0,$tagnum)];

Second, the directory of fonts is read, and a random one is pulled from the list. You need to make sure to have the ./ before the directory and filenames (assuming your font directory is a subdirectory of your PHP page).

  //get random font from ttf directory
  $dir = opendir("./ttf/");
  $i = 0;
  while($theFile = readdir($dir)) {
    if($theFile != "." && $theFile != "..") {
      $array[$i] = $theFile;
      $i++;
    }
  }
 
  closedir($dir);
  $fontnum = count($array)-1;
 
  $ttfLine = $array[rand(0,$fontnum)];
 
  $ttf = "./ttf/" . $ttfLine;

Finally, we invoke the function to create the image, passing to it both the tagline and the font file.

  //draw the image
  $text->draw($line, $ttf);
?>

Now, this is a little modified from the original code. The code has the font and text variables set to static values. I wanted my code to randomly select both a font file and a tagline, so I stuck a bunch of fonts in a folder called ttf, and created a file named tags.txt with several taglines.

The ttf directory and tags.txt are both located in the same directory as the PHP file containing this code. Each tagline is on a separate line in the text file, so the loop looks for line breaks to indicate individual taglines.

You can call to the image just like you would any normal image file:

#tagline {
  background: url(img/tagline.php) no-repeat;
}

You’ll quickly discover that since IE 6 doesn’t support transparent PNGs, your image won’t look very good. This is easily solved with a little hack that I discovered at twinhelix.com1). I couldn’t get the repeating background feature of the beta version to work properly, but it worked beautifully for this generated image.

IE 7 and up support transparent PNGs natively, so you only need to apply this for IE 6 and 5.5. If you’re already properly identifying the client browser and adding the appropriate stylesheet, this is a breeze.

The possibilities for this script are pretty endless, and it can be tweaked to meet your specific needs. Happy coding!