I have modified and improved my original Image Banner script.
The newest version of this script allows for variable line heights,
centered text, drop shadows, background images, and more.
Here is an example:
The script is included here on this page, below.
The original article is here.
Note: This script requires NetPBM and ImageMagick installed on the
server for drop shadows to work.
header_image.php PHP Source
The following is the source for the image creation script. To
use it, call it in an HTML page like so: 
<?php
// image_header.php
// Generate dynamic text banner images using TTF fonts and PHP.
// Copyright 2003,2004 The Johns Hopkins University, All rights reserved.
// Author: Jeffrey D. Silverman
// Date: 14-January-2004
// Description:
// Use this script to generate graphic headers.
// Pass in a value for "$text" to print.
// Example usage:
// <img src='header_image.php?text=this%20is%20the%20text' width='300' height='23'>
//
// CONFIGURATION - Change these items manually here or pass in values via the URL
// ITEMS that can be passed in (optionally) via the URL are:
// fontdir
// bg_b
// bgcolor
// bg_g
// bgimage
// bg_r
// center_x
// center_y
// cols
// drop_shadow
// fg_b
// fgcolor
// fg_g
// fg_r
// font
// fontdir
// image_height
// image_width
// lineheight
// linewidth
// shadow_offset_x
// shadow_offset_y
// text
// x
// y
define("fontdir", "." ); // Important! This MUST be a real directory for the script to work!
define("font", "arial.ttf" ); // Important! This MUST be a real TTF or PostScript font file for the script to work!
define("TEMP_IMAGE_DIR", "header_image_temp" );
define("image_width", 100 );
define("image_height", 23 );
define("lineheight", 12 );
define("x", 10 );
define("y", lineheight );
define("cols", 1 );
// Text (foreground) color, RGB values 0-255
define("fg_r", 0 );
define("fg_g", 0 );
define("fg_b", 0 );
// Background color, RGB values 0-255
define("bg_r", 255 );
define("bg_g", 255 );
define("bg_b", 255 );
# Configuration of external programs
# You MUST put the full path to the proper programs here. Not doing so will disable
# the drop-shadow functionality
define("GDTOPNG", "/usr/local/bin/gdtopng");
define("MOGRIFY", "/usr/bin/mogrify");
// END OF CONFIGURATION
// DO NOT CHANGE BELOW THIS LINE //////////////////////////
if (! is_dir(TEMP_IMAGE_DIR) ) mkdir(TEMP_IMAGE_DIR);
// Check for saved images based on MD5 Hash of the $_SERVER['QUERY_STRING']
$saved_image = TEMP_IMAGE_DIR . "/" . md5($_SERVER['QUERY_STRING']) . ".png";
if (is_file( $saved_image)){
header("Content-type: image/png");
echo file_get_contents($saved_image);
exit;
} else {
$fontdir = $_REQUEST['fontdir'] ? $_REQUEST['fontdir'] : fontdir;
$font = $_REQUEST['font'] ? $_REQUEST['font'] : font;
$image_width = $_REQUEST['image_width'] ? $_REQUEST['image_width'] : image_width;
$image_height = $_REQUEST['image_height'] ? $_REQUEST['image_height'] : image_height;
$lineheight = $_REQUEST['lineheight'] ? $_REQUEST['lineheight'] : lineheight;
$x = $_REQUEST['x'] ? $_REQUEST['x'] : x;
// $y = $_REQUEST['y'] ? $_REQUEST['y'] : $_REQUEST['lineheight'] ? $_REQUEST['lineheight'] : lineheight;
$y = $_REQUEST['y'] ? $_REQUEST['y'] : y;
if ($_REQUEST['center_x']){
// x-offset = ((width of box) - (width of string)) / 2
$x = ( $image_width / 2 ) + ($image_height / 2 );
}
// Need to make this dynamic. Not all header images should break on multiple lines! (Notably the page_title)
if ($_REQUEST['linewidth']){
// linewidth MUST be passed in through the URL to split long lines.
// Otherwise the text will be all on one line.
$split_lines = preg_split("/(.{" . $_REQUEST['linewidth'] . "}\S*)\s/", $_REQUEST['text'], -1, PREG_SPLIT_DELIM_CAPTURE);
} else {
$split_lines = array($_REQUEST['text']);
}
$cols = $_REQUEST['cols'] ? $_REQUEST['cols'] : cols;
// Text color, RGB values 0-255
$fg_r = $_REQUEST['fg_r'] ? $_REQUEST['fg_r'] : fg_r;
$fg_g = $_REQUEST['fg_g'] ? $_REQUEST['fg_g'] : fg_g;
$fg_b = $_REQUEST['fg_b'] ? $_REQUEST['fg_b'] : fg_b;
// background color, RGB values 0-255
$bg_r = $_REQUEST['bg_r'] ? $_REQUEST['bg_r'] : bg_r;
$bg_g = $_REQUEST['bg_g'] ? $_REQUEST['bg_g'] : bg_g;
$bg_b = $_REQUEST['bg_b'] ? $_REQUEST['bg_b'] : bg_b;
// Background Image
$fgcolor = $_REQUEST['fgcolor'] ? $_REQUEST['fgcolor'] : false;
$bgcolor = $_REQUEST['bgcolor'] ? $_REQUEST['bgcolor'] : false;
$bgimage = $_REQUEST['bgimage'] ? $_REQUEST['bgimage'] : false;
// Sample colors...
// $white = imagecolorallocate($im, 255, 255, 255);
// $gray1 = imagecolorallocate($im, 204, 204, 204);
// $white = imagecolorallocate($im, 255, 255, 255);
// $orange = imagecolorallocate($im, 220, 210, 60);
// $dark_red = imagecolorallocate($im, 130, 28, 28);
// $black = imagecolorallocate($im, 0, 0, 0);
$filetypes = array(
"gif" => "gif"
, "jpg" => "jpeg"
, "jpeg" => "jpeg"
, "jpe" => "jpeg"
, "png" => "png"
);
// TO DEBUG. Set Content-tpye to text/plain
// header("Content-type: text/html");
header("Content-type: image/png");
if ($fgcolor) {
$fgcolor = preg_replace("/[^0-9A-Za-z]/", "", $fgcolor);
list($fg_r, $fg_g, $fg_b) = array(hexdec( substr($fgcolor, 0, 2) ),
hexdec( substr($fgcolor, 2, 2) ),
hexdec( substr($fgcolor, 4, 2) ));
}
if ($bgcolor) {
$bgcolor = preg_replace("/[^0-9A-Za-z]/", "", $bgcolor);
list($bg_r, $bg_g, $bg_b) = array(hexdec( substr($bgcolor, 0, 2) ),
hexdec( substr($bgcolor, 2, 2) ),
hexdec( substr($bgcolor, 4, 2) ));
}
// Hack/Bugfix:
$result = preg_replace("/&/", "&", $_REQUEST['text']); // fixes problem with & showing up inside images!
foreach ($split_lines as $line){
if (! preg_match("/^\s*$/", $line)) $lines[] = $line;
}
$no_lines = count($lines);
if ($_REQUEST['center_y']){
$center_offset = ($no_lines + 1);
$y = ($image_height / 2 ) - ( (($no_lines / 2) * $lineheight) - $lineheight );
}
// $lines = preg_split("/\n/", $result);
$result = preg_replace("/\n/", "\r\n", $result);
$im = imagecreate($image_width, $image_height);
$im_shadow = imagecreate($image_width, $image_height);
if (is_file($bgimage)){
$suffix = array_pop(split("\.", $bgimage));
$func = "imagecreatefrom" . $filetypes[$suffix];
$im2 = $func($bgimage);
imagecopyresized($im, $im2, 0, 0, 0, 0, 1, 1, 1, 1);
}
$background = imagecolorallocate($im, $bg_r, $bg_g, $bg_b);
$black = imagecolorallocate($im, 0, 0, 0);
$textcolor = imagecolorallocate($im, $fg_r, $fg_g, $fg_b);
if ($_REQUEST['drop_shadow'] == 1 and (defined("GDTOPNG") and defined("MOGRIFY"))){
$y_orig = $y;
$shadow_offset_x = isset($_REQUEST['shadow_offset_x']) ? $_REQUEST['shadow_offset_x'] : 2;
$shadow_offset_y = isset($_REQUEST['shadow_offset_y']) ? $_REQUEST['shadow_offset_y'] : 2;
foreach ($lines as $string) {
$string = preg_replace("/^\s*/", "", $string);
imagefttext($im, $lineheight - 1, 0, $x+$shadow_offset_x, $y+$shadow_offset_y, $black, "$fontdir/$font", $string, array() );
$y += $lineheight;
}
$y = $y_orig;
$tempimage = md5(time() . "" . rand(0,10000000));
imagegd($im, "/tmp/$tempimage.gd");
system(GDTOPNG . " /tmp/$tempimage.gd /tmp/$tempimage.png");
system(MOGRIFY . " -blur 2 /tmp/$tempimage.png");
$im = imagecreatefrompng("/tmp/$tempimage.mgk");
unlink("/tmp/$tempimage.png");
unlink("/tmp/$tempimage.gd");
unlink("/tmp/$tempimage.mgk");
$textcolor = imagecolorallocate($im, $fg_r, $fg_g, $fg_b);
foreach ($lines as $string) {
$string = preg_replace("/^\s*/", "", $string);
imagefttext($im, $lineheight - 1, 0, $x, $y, $textcolor, "$fontdir/$font", $string, array() );
$y += $lineheight;
}
} else {
foreach ($lines as $string) {
$string = preg_replace("/^\s*/", "", $string);
imagefttext($im, $lineheight - 1, 0, $x, $y, $textcolor, "$fontdir/$font", $string, array() );
$y += $lineheight;
}
}
imagepng($im);
imagepng($im, $saved_image);
imagedestroy($im);
}
?>