PHP TIPS & TRICKS - Dynamic Image Generation With PHP

This tutorial is designed for the intermediate PHP user.

(For more about Zend Technologies, please visit www.zend.com)

Introduction

PHP lets you do a lot more than simply generating HTML content, it is also a great tool to dynamically generate images on-the-fly. For example, you can create buttons with text from a database, create graphical displays of statistics from log files and enhance business data with graphs and diagrams

This tutorial shows you how to use the GD library to dynamically create images on your site. The GD library is an external module which is accessed by a native PHP module.

Goals of the Tutorial

In this tutorial you will learn the following:

  • About headers
  • How to use the basic GD command set to dynamically generate images with PHP.
  • How to make use of the HTML header fields to trick your browser into thinking your PHP files are actually images

Background Information

The key to controlling how data is displayed is the HTML header. Before sending out the actual data of your webpage, each Web server sends a header which is formatted according to the HTTP protocol rules. It denotes the kind of data, the last modification date, information about the document size and such (see for example RFC 2068, which can be retrieved at any RFC archive like ftp://ftp.isi.edu/in-notes/rfc2068.txt).

Creating a Header

By default, PHP creates at least one header field for you: the Content-type header which marks the output of your script as HTML data.

< ? php

print("Hello world!");

? >

$ > php hello.php

Content-type: text/html

Hello world!

$ > _

As you can see, PHP prints Content-type: text/html and a blank line before starting to print the output of this script. Everything before the blank line is defined as being the HTTP header. The header is interpreted by your browser and doesn't show up in the page content.

The default header Content-type: text/html is a MIME-type, an element of a standardized catalog of content types, and denotes the following content as HTML that your browser is supposed to render. If we'd change this to "Content-type: text/plain" (another MIME-type), the browser would not render the data but interprete it as plain text - and simply display it "as is".

PHP allows you to change the content type field in the header with the header( ) function.

header("Content-type: image/jpeg"); // mark the following content as JPEG image

header("Content-type: application/zip"); // mark the following content as ZIP file

Of course, this makes sense only if the data you send is of the specified type. Sending a header for a ZIP file and printing "Hello world!" after that would confuse your browser or any other application handling your ZIPs.

But the point is that choosing your own content type headers opens the door to another dimension of server-side scripting. You can make PHP generate any form of data which is specified in the catalogue of MIME types (a huge variety of sound, video and text formats), This tutorial is focused on the dynamic generation of images.

Note
Optionally, your webserver may add a few fields to the header, but these are not interesting at the moment.

Preparing Images for PHP

Image generation in PHP can easily be done by using Thomas Boutell's GD library. (see http://www.boutell.com/gd) Support for this library can be compiled into PHP using the --with-gd option after which you can access the GD functionality with a large set of native functions. In case you would also like to use the FreeType functions, you require the FreeType library (for ImageTTFText( ) and ImageTTFBox( )) that you can retrieve at http://www.freetype.org. The installation of both should be straight forward when following the documentation.

Generating Images with PHP

Basically, image generation is done in three steps:

  • allocating an image
  • drawing into the allocated space
  • releasing the allocated data in picture format to the browser

Allocating an image is done using GD's ImageCreate( ) function. As parameters this function takes the image width and height in pixels, the return value consists of a handle that you require to refer to this allocated image when doing further drawing (or similar) operations.

After allocating an image, you must allocate colors. The GD library needs to know which colors are in use before it can generate the final image data, thus it requires you to register all colors that you want to use. Registering colors can be done using ImageColorAllocate( ). As arguments this function takes the handle of the image for which you want to register a color and the RGB (red, green, blue) values of the specific color you would like to add. The return value is a handle for the color which allows you to reference it later on when drawing.

Example:

$ colorHandle = imageColorAllocate($ image, 192, 192, 192) // allocate color

imageFilledRectangle($ image, 0, 0, $ width - 1, $ height - 1, $ colorHandle); // use it for drawing

There are a large number of PHP functions for drawing the image. Covering all the functions is beyond the scope of this tutorial. To get a complete list of the drawing function, take a look at the appropriate section in the PHP manual. (direct link is here)

Finally, releasing the picture to the browser is done with a single call to either ImagePNG( ) or ImageGIF( ). The latter is not supported in current versions of the GD library due to licensing problems with the compression code. ImagePNG( ) converts the internal image data to a PNG file and writes it directly to the client. ImageGIF( ) does the same, except for that it creates a GIF image.

Before calling either ImageGIF( ) or ImagePNG( ), set the content header as shown in the following table.

Image Type Content Header
PNG "Content-type: image/png"
GIF "Content-type: image/gif"

NOTE

The content type headers refer to the entire document. This means that once you have specified an image content type, you cannot print regular text from that point on. Thus, after the first byte has been sent out, you cannot change the header anymore! This means you must first call header( ) and then do the rest of your script processing, otherwise you receive a warning message. If you do not specify a content type header at all, PHP automatically sends the header type "text/html" for you as soon as the first character is sent to the browser.

How It Works

To show you how to actually make use of the GD library, this tutorial shows a sample script to print out your biorythm. Following, each step of the source is commented and explained.

The theory of the biorythm says that the emotional, physical and intellectual state of each person has low and high passes within regular intervals. With birth, all three curves start at zero and then swing with a certain cycle through your life. If a curve is above the middle line, it is in an active phase, if it is below it is in a passive phase. If it crosses the middle line this means a critical day: you're prone to intellectual, physical or emotional "catastrophes". When all three curves cross the line you should therefore try to avoid to work on important PHP projects.

Each curve has different cycles:

  • Physical: 23 days
  • Emotional: 28 days
  • Intellectual: 33 days

All curves are sinusoid, so drawing them can be reduced to a simple call to sin( ) with a little bit of math around it.

Setting the Birthday

This step retrieves the user's birthdate and calculates the number of days the person has been alive to date. The result can then be used to directly calculate the phase of the sine curves for the biorhythm.

Code Flow

  • Checks whether a birthdate has been specified.
  • If not, it displays a form to allow the user to input one.
  • After checking date validity and specifying diagram parameters, calculates the number of days the person has been alive to date using the Julian calendar.

if(!isset($ birthdate))

{

/* */

}

$ daysGone = abs(gregorian ToJD($ birthMonth, $ birthDay, $ birthYear)

-

gregorian ToJD(date( "m"), date( "d"), date( "Y")));

Tips

The date calculation uses Julian dates, which count absolute number of days, not a split date into years, months and days like Gregorian dates do. In order to determine the number of days the person is alive, both today's date and the person's birthdate have to be converted to Julian dates. Calculating the difference gives the correct result.

Note

The function gregorian ToJD( ) has been reimplemented in the PHP source code. Usually, this function is also available in PHP's calendaring module, but at the time of this writing, it did not work in the most up-to-date version of PHP 4. So, in order to assure a working program with all PHP versions, this function is now "handcrafted". If you still want to use the calendaring module, uncomment the function here.

Preparing the Image

Code Flow

Preparing the image is done with a few calls to a set of GD's functions. The following code:

  • Names and creates a new $ image of the desired size (in pixels).
  • Allocate the colors for the drawing $ image.
  • Creates a rectangle with a background color to draw the image. (Clears the image space.)

// create image

$ image = imageCreate($ diagramWidth , $ diagramHeight);

// allocate all required colors

$ colorBackgr = imageColorAllocate($ image, 192, 192, 192);

$ colorForegr = imageColorAllocate($ image, 255, 255, 255);

$ colorGrid = imageColorAllocate($ image, 0, 0, 0);

$ colorCross = imageColorAllocate($ image, 0, 0, 0);

$ colorPhysical = imageColorAllocate($ image, 0, 0, 255);

$ colorEmotional = imageColorAllocate($ image, 255, 0, 0);

$ colorIntellectual = imageColorAllocate($ image, 0, 255, 0);

// clear the image space with the background color

imageFilledRectangle($ image, 0, 0, $ width - 1, $ height - 1, $ colorBackgr);

Tips

It's a good idea to create a colored rectangle to serve as a background for drawing the picture. This ensures that the image will be drawn on a blank space of the color you want. It is also useful for creating a transparency effect, as this tutorial discusses later.

Note

This tutorial does not include any error checking, which is bad coding style, but is used for clarity in the tutorial. In a "real" script, there should be error checking and exception handling to make sure that everything works as expected, and to give some sort of notification to the user if there is an error.

Drawing Boundaries and Adding Text

ImageString( ) writes the specified text string into the image using a position and size. The first two strings will appear at the top of the diagram and display the birthdate and today's date, while the other three will appear at the bottom and give color information for the three curves. Working with the predefined color handles makes it easy to denote which color you want to use.

Code Flow

  • Draws the diagram's boundaries using a rectangle and the middle cross using two simple lines.
  • Places descriptive text in the diagram.

// draw rectangle around diagram (marks its boundaries)

imageRectangle($ image, 0, 0, $ diagramWidth - 1, $ diagramHeight - 20,

$ colorGrid);

// draw middle cross

imageLine($ image, 0, ($ diagramHeight - 20) / 2, $ diagramWidth,

(

$ diagramHeight - 20) / 2, $ colorCross);

imageLine($ image, $ diagramWidth / 2, 0, $ diagramWidth / 2, $ diagramHeight - 20,

$ colorCross);

imageString($ image, 3, 10, 10, "Birthday: $ birthDay.$ birthMonth.$ birthYear",

$ colorCross);

imageString($ image, 3, 10, 26, "Today: ". date( "d.m.Y"), $ colorCross);

imageString($ image, 3, 10, $ diagramHeight - 42, "Physical", $ colorPhysical);

imageString($ image, 3, 10, $ diagramHeight - 58, "Emotional", $ colorEmotional);

imageString($ image, 3, 10, $ diagramHeight - 74, "Intellectual",

$ colorIntellectual);

Figure 1. The final biorhythm image

<?PHP print Create_Archive_Image_Tag('<% Content_ID %>', "php4.jpg", "", " width=\"331\""); ?>

Drawing the Curves

All the curves are sine waves differing only in their period, drawing a "rhythm" has been abstracted into the function drawRythm( ). The curve is drawn by calculating the biorhythm values on each day from the start date to the current date, and drawing a line from the value on one day to the value on the next day. The biorhthm value is a Y position on the diagram. The lines are drawing in intervals of one day, using as connection points the X/Y coordinates calculated for each day.

Code Flow

  • Fixes the start angle of the sine wave based on the number of days the person is alive ($ daysGone) and the period of the curve.
  • Calls the drawRhythm function to draw the curve three times: one with each parameter set.
  • Within for loop, draws a line from each point to the next point and saves the coordinates for the next run of the for loop.

drawRhythm($ daysGone, 23, $ colorPhysical);

drawRhythm($ daysGone, 28, $ colorEmotional);

drawRhythm($ daysGone, 33, $ colorIntellectual);

for(

$ x = 0; $ x &lt;= $ daysToShow; $ x++)

{

// calculate phase of curve at this day, then Y value

// within diagram

$ phase = (($ centerDay + $ x) % $ period) / $ period * 2 * pi( );

$ y = 1 - sin($ phase) * (float)$ plotScale + (float)$ plotCenter;

// draw line from last point to current point

if($ x &gt; 0)

imageLine($ image, $ oldX, $ oldY, $ x * $ diagramWidth / $ daysToShow,

$ y, $ color);

// save current X/Y coordinates as start point for next line

$ oldX = $ x * $ diagramWidth / $ daysToShow;

$ oldY = $ y;

}

Tips

It would be better to not draw lines in day intervals on the diagram but to use smaller steps and interpolate between the coordinates, eventually even