3. Outputting the final graphic 4. Cleaning up resources We’ll begin by looking at a very simple image creation script. This script is shown in Listing 19.1. LISTING 19.1 simplegraph.php —Outputs a Simple Line Graph with the Label Sales <? // set up image $height = 200; $width = 200; $im = ImageCreate($width, $height); $white = ImageColorAllocate ($im, 255, 255, 255); $black = ImageColorAllocate ($im, 0, 0, 0); // draw on image ImageFill($im, 0, 0, $black); ImageLine($im, 0, 0, $width, $height, $white); ImageString($im, 4, 50, 150, “Sales”, $white); // output image Header (“Content-type: image/png”); ImagePng ($im); // clean up ImageDestroy($im); ?> The output from running this script is shown in Figure 19.1. We’ll walk through the steps of creating this image one by one. Creating a Canvas Image To begin building or changing an image in PHP, you will need to create an image identifier. There are two basic ways to do this. One is to create a blank canvas, which you can do with a call to the ImageCreate() function, as we have done in this script with the following: $im = ImageCreate($width, $height); You need to pass two parameters to ImageCreate(). The first is the width of the new image, and the second is the height of the new image. The function will return an identifier for the new image. (These work a lot like file handles.) Generating Images C HAPTER 19 19 G ENERATING IMAGES 405 24 7842 CH19 3/6/01 3:42 PM Page 405 FIGURE 19.1 The script draws a black background and then adds a line and a text label for the image. An alternative way is to read in an existing image file that you can then filter, resize, or add to. You can do this with one of the functions ImageCreateFromPNG(), ImageCreateFromJPEG(), or ImageCreateFromGIF(), depending on the file format you are reading in. Each of these takes the filename as a parameter, as in, for example, $im = ImageCreateFromPNG(“baseimage.png”); An example is shown later in this chapter using existing images to create buttons on-the-fly. Drawing or Printing Text onto the Image There are really two stages to drawing or printing text on the image. First, you must select the colors in which you want to draw. As you probably already know, colors to be displayed on a computer monitor are made up of different amounts of red, green, and blue light. Image formats use a color palette that consists of a specified subset of all the possible combinations of the three colors. To use a color to draw in an image, you need to add this color to the image’s palette. You must do this for every color you want to use, even black and white. You can select colors for your image by calling the ImageColorAllocate() function. You need to pass your image identifier and the red, green, and blue (RGB) values of the color you want to draw into the function. In Listing 19.1, we are using two colors: black and white. We allocate these by calling $white = ImageColorAllocate ($im, 255, 255, 255); $black = ImageColorAllocate ($im, 0, 0, 0); Advanced PHP Techniques P ART IV 406 24 7842 CH19 3/6/01 3:42 PM Page 406 The function returns a color identifier that we can use to access the color later on. Second, to actually draw into the image, a number of different functions are available, depend- ing on what you want to draw—lines, arcs, polygons, or text. The drawing functions generally require the following as parameters: • The image identifier • The start and sometimes the end coordinates of what you want to draw • The color you want to draw in • For text, the font information In this case, we used three of the drawing functions. Let’s look at each one in turn. First, we painted a black background on which to draw using the ImageFill() function: ImageFill($im, 0, 0, $black); This function takes the image identifier, the start coordinates of the area to paint (x and y), and the color to fill in as parameters. Generating Images C HAPTER 19 19 G ENERATING IMAGES 407 One thing to note is that the coordinates of the image start from the top-left corner, which is x=0, y=0. The bottom-right corner of the image is x=$width, y=$height. This is the opposite of typical graphing conventions, so beware! NOTE Next, we’ve drawn a line from the top-left corner (0, 0) to the bottom-right corner ($width, $height) of the image: ImageLine($im, 0, 0, $width, $height, $white); This function takes the image identifier, the start point x and y for the line, the end point, and then the color, as parameters. Finally, we add a label to the graph: ImageString($im, 4, 50, 150, “Sales”, $white); The ImageString() function takes some slightly different parameters. The prototype for this function is int imagestring (int im, int font, int x, int y, string s, int col) 24 7842 CH19 3/6/01 3:42 PM Page 407 It takes as parameters the image identifier, the font, the x and y coordinates to start writing the text, the text to write, and the color. The font is a number between 1 and 5. These represent a set of built-in fonts. As an alternative to these, you can use TrueType fonts, or PostScript Type 1 fonts. Each of these font sets has a corresponding function set. We will use the TrueType functions in the next example. A good reason for using one of the alternative font function sets is that the text written by ImageString() and associated functions, such as ImageChar() (write a character to the image) is aliased. The TrueType and PostScript functions produce anti-aliased text. If you’re not sure what the difference is, look at Figure 19.2. Where curves or angled lines appear in the letters, the aliased text appears jagged. The curve or angle is achieved by using a “staircase” effect. In the anti-aliased image, when there are curves or angles in the text, pixels in colors between the background and the text color are used to smooth the text’s appearance. Advanced PHP Techniques P ART IV 408 FIGURE 19.2 Normal text appears jagged, especially in a large font size. Anti-aliasing smooths the curves and corners of the letters. Outputting the Final Graphic You can output an image either directly to the browser, or to a file. In this example, we’ve output the image to the browser. This is a two-stage process. First, we need to tell the Web browser that we are outputting an image rather than text or HTML. We do this by using the Header() function to specify the MIME type of the image: Header (“Content-type: image/png”); Normally when you retrieve a file in your browser, the MIME type is the first thing the Web server sends. For an HTML or PHP page (post execution), the first thing sent will be Content-type: text/html 24 7842 CH19 3/6/01 3:42 PM Page 408 This tells the browser how to interpret the data that follows. In this case, we want to tell the browser that we are sending an image instead of the usual HTML output. We can do this using the Header() function, which we have not yet discussed. This function sends raw HTTP header strings. Another typical application of this is to do HTTP redirects. These tell the browser to load a different page instead of the one requested. They are typically used when a page has been moved. For example, Header (“Location: http://www.domain.com/new_home_page.html “); An important point to note when using the Header() function is that it cannot be executed if an HTTP header has already been sent for the page. PHP will send an HTTP header automati- cally for you as soon as you output anything to the browser. Hence, if you have any echo state- ments, or even any whitespace before your opening PHP tag, the headers will be sent, and you will get a warning message from PHP when you try to call Header(). However, you can send multiple HTTP headers with multiple calls to the Header() function in the same script, although they must all appear before any output is sent to the browser. After we have sent the header data, we output the image data with a call to ImagePng ($im); This sends the output to the browser in PNG format. If you wanted it sent in a different format, you could call ImageJPEG()—if JPEG support is enabled—or ImageGIF() —if you have an older version of gd. You would also need to send the corresponding header first; that is, either Header (“Content-type: image/jpeg”); or Header (“Content-type: image/gif”); The second option you can use, as an alternative to all the previous ones, is to write the image to a file instead of to the browser. You can do this by adding the optional second parameter to ImagePNG() (or a similar function for the other supported formats): ImagePNG($im, $filename); Remember that all the usual rules about writing to a file from PHP apply (for example, having permissions set up correctly). Generating Images C HAPTER 19 19 G ENERATING IMAGES 409 24 7842 CH19 3/6/01 3:42 PM Page 409 Cleaning Up When you’re done with an image, you should return the resources you have been using to the server by destroying the image identifier. You can do this with a call to ImageDestroy(): ImageDestroy($im); Using Automatically Generated Images in Other Pages Because a header can only be sent once, and this is the only way to tell the browser that we are sending image data, it is slightly tricky to embed any images we create on-the-fly in a regular page. Three ways you can do it are as follows: 1. You can have an entire page consist of the image output, as we did in the previous example. 2. You can write the image out to a file as previously mentioned, and then refer to it with a normal <IMG> tag. 3. You can put the image production script in an image tag. We have covered methods 1 and 2 already. Let’s briefly look at method 3. To use this method, you include the image inline in HTML by having an image tag along the lines of the following: <img src=”simplegraph.php” height=200 width=200 alt=”Sales going down”> Instead of putting in a PNG, JPEG, or GIF directly, put in the PHP script that generates the image in the SRC tag. This will be retrieved and the output added inline, as shown in Figure 19.3. Using Text and Fonts to Create Images We’ll look at a more complicated example. It is useful to be able to create buttons or other images for your Web site automatically. You can build simple buttons based on a rectangle of background color using the techniques we’ve already discussed. In this example, however, we’ll generate buttons using a blank button template that allows us to have features like beveled edges and so on, which are a good deal easier to generate using Photoshop, the GIMP, or some other graphics tool. With the image library in PHP, we can begin with a base image and draw on top of that. Advanced PHP Techniques P ART IV 410 24 7842 CH19 3/6/01 3:42 PM Page 410 FIGURE 19.3 The dynamically produced inline image appears the same as a regular image to the end user. We will also use TrueType fonts so that we can use anti-aliased text. The TrueType font func- tions have their own quirks, which we’ll discuss. The basic process is to take some text and generate a button with that text on it. The text will be centered both horizontally and vertically on the button, and will be rendered in the largest font size that will fit on the button. We’ve built a front end to the button generator for testing and experimenting. This interface is shown in Figure 19.4. (We have not included the HTML for this form here as it is very simple, but you can find it on the CD in design_button.html.) You could use this type of interface for a program to automatically generate Web sites. You could also call the script we write in an inline fashion, to generate all a Web site’s buttons on- the-fly! Typical output from the script is shown in Figure 19.5. Generating Images C HAPTER 19 19 G ENERATING IMAGES 411 24 7842 CH19 3/6/01 3:42 PM Page 411 FIGURE 19.4 The front end lets a user choose the button color and type in the required text. Advanced PHP Techniques P ART IV 412 FIGURE 19.5 A button generated by the make_button.php script. The button is generated by a script called make_button.php. This script is shown in Listing 19.2. LISTING 19.2 make_button.php —This Script Can Be Called from the Form in design_button.html or from Within an HTML Image Tag <? // check we have the appropriate variable data // variables are button-text and color 24 7842 CH19 3/6/01 3:42 PM Page 412 if (empty($button_text) || empty($color)) { echo “Could not create image - form not filled out correctly”; exit; } // create an image of the right background and check size $im = imagecreatefrompng (“$color-button.png”); $width_image = ImageSX($im); $height_image = ImageSY($im); // Our images need an 18 pixel margin in from the edge image $width_image_wo_margins = $width_image - (2 * 18); $height_image_wo_margins = $height_image - (2 * 18); // Work out if the font size will fit and make it smaller until it does // Start out with the biggest size that will reasonably fit on our buttons $font_size = 33; do { $font_size ; // find out the size of the text at that font size $bbox=imagettfbbox ($font_size, 0, “arial.ttf”, $button_text); $right_text = $bbox[2]; // right co-ordinate $left_text = $bbox[0]; // left co-ordinate $width_text = $right_text - $left_text; // how wide is it? $height_text = abs($bbox[7] - $bbox[1]); // how tall is it? } while ( $font_size>8 && ( $height_text>$height_image_wo_margins || $width_text>$width_image_wo_margins ) ); if ( $height_text>$height_image_wo_margins || $width_text>$width_image_wo_margins ) { // no readable font size will fit on button echo “Text given will not fit on button.<BR>”; } else Generating Images C HAPTER 19 19 G ENERATING IMAGES 413 LISTING 19.2 Continued 24 7842 CH19 3/6/01 3:42 PM Page 413 { // We have found a font size that will fit // Now work out where to put it $text_x = $width_image/2.0 - $width_text/2.0; $text_y = $height_image/2.0 - $height_text/2.0 ; if ($left_text < 0) $text_x += abs($left_text); // add factor for left overhang $above_line_text = abs($bbox[7]); // how far above the baseline? $text_y += $above_line_text; // add baseline factor $text_y -= 2; // adjustment factor for shape of our template $white = ImageColorAllocate ($im, 255, 255, 255); ImageTTFText ($im, $font_size, 0, $text_x, $text_y, $white, “arial.ttf”, $button_text); Header (“Content-type: image/png”); ImagePng ($im); } ImageDestroy ($im); ?> This is one of the longest scripts we’ve looked at so far. Let’s step through it section by sec- tion. We begin with some basic error checking, and then set up the canvas on which we’re going to work. Setting Up the Base Canvas In Listing 19.2, rather than starting from scratch, we will start with an existing image for the button. We have a choice of three colors in the basic button: red (red-button.png), green (green-button.png), and blue (blue-button.png). The user’s chosen color is stored in the $color variable from the form. We begin by setting up a new image identifier based on the appropriate button: $im = imagecreatefrompng (“$color-button.png”); Advanced PHP Techniques P ART IV 414 LISTING 19.2 Continued 24 7842 CH19 3/6/01 3:42 PM Page 414 . the following: <img src=”simplegraph .php height=200 width=200 alt=”Sales going down”> Instead of putting in a PNG, JPEG, or GIF directly, put in the PHP script that generates the image in. required text. Advanced PHP Techniques P ART IV 412 FIGURE 19.5 A button generated by the make_button .php script. The button is generated by a script called make_button .php. This script is shown in Listing. generate using Photoshop, the GIMP, or some other graphics tool. With the image library in PHP, we can begin with a base image and draw on top of that. Advanced PHP Techniques P ART IV 410 24