The function ImageCreateFromPNG() takes the filename of a PNG as a parameter, and returns a new image identifier for an image containing a copy of that PNG. Note that this does not modify the base PNG in any way. We can use the ImageCreateFromJPEG() and ImageCreateFromGIF()functions in the same way if the appropriate support is installed. Generating Images C HAPTER 19 19 G ENERATING IMAGES 415 The call to ImageCreateFromPNG() only creates the image in memory. To save the image to a file or output it to the browser, we must call the ImagePNG() function. We’ll come to that in a minute, but we have other work to do with our image first. NOTE Fitting the Text onto the Button We have some text typed in by the user stored in the $button_text variable. What we want to do is print that on the button in the largest font size that will fit. We do this by iteration, or strictly speaking, by iterative trial and error. We start by setting up some relevant variables. The first two are the height and width of the button image: $width_image = ImageSX($im); $height_image = ImageSY($im); The second two represent a margin in from the edge of the button. Our button images are beveled, so we’ll need to leave room for that around the edges of the text. If you are using dif- ferent images, this number will be different! In our case, the margin on each side is around 18 pixels. $width_image_wo_margins = $width_image - (2 * 18); $height_image_wo_margins = $height_image - (2 * 18); We also need to set up the initial font size. We start with 32 (actually 33, but we’ll decrement that in a minute) because this is about the biggest font that will fit on the button at all: $font_size = 33; Now we loop, decrementing the font size at each iteration, until the submitted text will fit on the button reasonably: do { $font_size ; 24 7842 CH19 3/6/01 3:42 PM Page 415 // 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 ) ); This code tests the size of the text by looking at what is called the bounding box of the text. We do this using the ImageGetTTFBBox() function, which is one of the TrueType font func- tions. We will, after we have figured out the size, print on the button using a TrueType font and the ImageTTFText() function. The bounding box of a piece of text is the smallest box you could draw around the text. An example of a bounding box is shown in Figure 19.6. Advanced PHP Techniques P ART IV 416 FIGURE 19.6 Coordinates of the bounding box are given relative to the baseline. The origin of the coordinates is shown here as (0,0). To get the dimensions of the box, we call $bbox=imagettfbbox ($font_size, 0, “arial.ttf”, $button_text); This call says, “For given font size $font_size, with text slanted on an angle of zero degrees, using the TrueType font Arial, tell me the dimensions of the text in $button_text.” Note that you actually need to pass the path to the file containing the font into the function. In this case, it’s in the same directory as the script (the default), so we haven’t specified a longer path. The function returns an array containing the coordinates of the corners of the bounding box. The contents of the array are shown in Table 19.1. 24 7842 CH19 3/6/01 3:42 PM Page 416 TABLE 19.1 Contents of the Bounding Box Array Array Index Contents 0 X coordinate, lower-left corner 1 Y coordinate, lower-left corner 2 X coordinate, lower-right corner 3 Y coordinate, lower-right corner 4 X coordinate, upper-right corner 5 Y coordinate, upper-right corner 6 X coordinate, upper-left corner 7 Y coordinate, upper-left corner To remember what the contents of the array are, just remember that the numbering starts at the bottom-left corner of the bounding box and works its way around counterclockwise. There is one tricky thing about the values returned from the ImageTTFBBox() function. They are coordinate values, specified from an origin. However, unlike coordinates for images, which are specified relative to the top-left corner, they are specified relative to a baseline. Look at Figure 19.6 again. You will see that we have drawn a line along the bottom of most of the text. This is known as the baseline. Some letters hang below the baseline, such as y in this example. These are called descenders. The left side of the baseline is specified as the origin of measurements—that is, X coordinate 0 and Y coordinate 0. Coordinates above the baseline have a positive X coordinate and coordi- nates below the baseline have a negative X coordinate. In addition to this, text might actually have coordinate values that sit outside the bounding box. For example, the text might actually start at an X coordinate of –1. What this all adds up to is the fact that care is required when performing calculations with these numbers. We work out the width and height of the text as follows: $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? Generating Images C HAPTER 19 19 G ENERATING IMAGES 417 24 7842 CH19 3/6/01 3:42 PM Page 417 After we have this, we test the loop condition: } while ( $font_size>8 && ( $height_text>$height_image_wo_margins || $width_text>$width_image_wo_margins ) ); We are testing two sets of conditions here. The first is that the font is still readable—there’s no point in making it much smaller than 8 point because the button becomes too difficult to read. The second set of conditions tests whether the text will fit inside the drawing space we have for it. Next, we check to see whether our iterative calculations found an acceptable font size or not, and report an error if not: 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>”; } Positioning the Text If all was okay, we next work out a base position for the start of the text. This is the midpoint of the available space. $text_x = $width_image/2.0 - $width_text/2.0; $text_y = $height_image/2.0 - $height_text/2.0 ; Because of the complications with the baseline relative co-ordinate system, we need to add some correction factors: 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 These correction factors allow for the baseline and a little adjustment because our image is a bit “top heavy.” Advanced PHP Techniques P ART IV 418 24 7842 CH19 3/6/01 3:42 PM Page 418 Writing the Text onto the Button After that, it’s all smooth sailing. We set up the text color, which will be white: $white = ImageColorAllocate ($im, 255, 255, 255); We can then use the ImageTTFText() function to actually draw the text onto the button: ImageTTFText ($im, $font_size, 0, $text_x, $text_y, $white, “arial.ttf”, $button_text); This function takes quite a lot of parameters. In order, they are the image identifier, the font size in points, the angle we want to draw the text at, the starting X and Y coordinates of the text, the text color, the font file, and, finally, the actual text to go on the button. Generating Images C HAPTER 19 19 G ENERATING IMAGES 419 The font file needs to be available on the server, and is not required on the client’s machine because she will see it as an image. By default, the function will look for the file in the same directory that the script is running in. Alternatively, you can specify a path to the font. NOTE Finishing Up Finally, we can output the button to the browser: Header (“Content-type: image/png”); ImagePng ($im); Then it’s time to clean up resources and end the script: ImageDestroy ($im); That’s it! If all went well, we should now have a button in the browser window that looks simi- lar to the one you saw in Figure 19.5. Drawing Figures and Graphing Data In that last application, we looked at existing images and text. We haven’t yet looked at an example with drawing, so we’ll do that now. In this example, we’ll run a poll on our Web site to test whom users will vote for in a fictitious election. We’ll store the results of the poll in a MySQL database, and draw a bar chart of the results using the image functions. 24 7842 CH19 3/6/01 3:42 PM Page 419 Graphing is the other thing these functions are primarily used for. You can chart any data you want—sales, Web hits, or whatever takes your fancy. For this example, we have spent a few minutes setting up a MySQL database called poll. It contains one table called poll_results, which holds the candidates’ names in the candidate col- umn, and the number of votes they have received in the num_votes column. We have also cre- ated a user for this database called poll, with password poll. This takes about five minutes to set up, and you can do this by running the SQL script shown in Listing 19.3. You can do this piping the script through a root login using mysql -u root -p < pollsetup.sql Of course, you could also use the login of any user with the appropriate MySQL privileges. LISTING 19.3 pollsetup.sql —Setting Up the Poll Database create database poll; use poll; create table poll_results ( candidate varchar(30), num_votes int ); insert into poll_results values (‘John Smith’, 0), (‘Mary Jones’, 0), (‘Fred Bloggs’, 0) ; grant all privileges on poll.* to poll@localhost identified by ‘poll’; This database contains three candidates. We provide a voting interface via a page called vote.html. The code for this page is shown in Listing 19.4. LISTING 19.4 vote.html—Users Can Cast Their Votes Here <html> <head> <title>Polling</title> <head> <body> <h1>Pop Poll</h1> <p>Who will you vote for in the election?</p> <form method=post action=”show_poll.php”> Advanced PHP Techniques P ART IV 420 24 7842 CH19 3/6/01 3:42 PM Page 420 <input type=radio name=vote value=”John Smith”>John Smith<br> <input type=radio name=vote value=”Mary Jones”>Mary Jones<br> <input type=radio name=vote value=”Fred Bloggs”>Fred Bloggs<br><br> <input type=submit value=”Show results”> </form> </body> The output from this page is shown in Figure 19.7. Generating Images C HAPTER 19 19 G ENERATING IMAGES 421 LISTING 19.4 Continued FIGURE 19.7 Users can cast their votes here, and clicking the submit button will show them the current poll results. The general idea is that, when users click the button, we will add their vote to the database, get all the votes out of the database, and draw the bar chart of the current results. Typical output after some votes have cast is shown in Figure 19.8. The script that generates this image is quite long. We have split it into four parts, and we’ll dis- cuss each part separately. Most of the script is familiar; we have looked at many MySQL examples similar to this. We have looked at how to paint a background canvas in a solid color, and how to print text labels on it. 24 7842 CH19 3/6/01 3:42 PM Page 421 FIGURE 19.8 Vote results are created by drawing a series of lines, rectangles, and text items onto a canvas. The new parts of this script relate to drawing lines and rectangles. We will focus our attention on these sections. Part 1 (of this four-part script) is shown in Listing 19.5.1. LISTING 19.5.1 showpoll.php—Part 1 Updates the Vote Database and Retrieves the New Results <? /******************************************* Database query to get poll info *******************************************/ // log in to database if (!$db_conn = @mysql_connect(“localhost”, “poll”, “poll”)) { echo “Could not connect to db<br>”; exit; }; @mysql_select_db(“poll”); if (!empty($vote)) // if they filled the form out, add their vote { $vote = addslashes($vote); $query = “update poll_results set num_votes = num_votes + 1 where candidate = ‘$vote’”; if(!($result = @mysql_query($query, $db_conn))) { echo “Could not connect to db<br>”; exit; } }; Advanced PHP Techniques P ART IV 422 24 7842 CH19 3/6/01 3:42 PM Page 422 // get current results of poll, regardless of whether they voted $query = “select * from poll_results”; if(!($result = @mysql_query($query, $db_conn))) { echo “Could not connect to db<br>”; exit; } $num_candidates = mysql_num_rows($result); // calculate total number of votes so far $total_votes=0; while ($row = mysql_fetch_object ($result)) { $total_votes += $row->num_votes; } mysql_data_seek($result, 0); // reset result pointer Part 1, shown in Listing 19.5.1, connects to the MySQL database, updates the votes according to what the user typed, and gets the new votes. After we have that information, we can begin making calculations in order to draw the graph. Part 2 is shown in Listing 19.5.2. LISTING 19.5.2 showpoll.php—Part 2 Sets Up All the Variables for Drawing /******************************************* Initial calculations for graph *******************************************/ // set up constants $width=500; // width of image in pixels - this will fit in 640x480 $left_margin = 50; // space to leave on left of image $right_margin= 50; // ditto right $bar_height = 40; $bar_spacing = $bar_height/2; $font = “arial.ttf”; $title_size= 16; // point $main_size= 12; // point $small_size= 12; // point $text_indent = 10; // position for text labels on left // set up initial point to draw from $x = $left_margin + 60; // place to draw baseline of the graph $y = 50; // ditto $bar_unit = ($width-($x+$right_margin)) / 100; // one “point” on the graph // calculate height of graph - bars plus gaps plus some margin $height = $num_candidates * ($bar_height + $bar_spacing) + 50; Generating Images C HAPTER 19 19 G ENERATING IMAGES 423 LISTING 19.5.1 Continued 24 7842 CH19 3/6/01 3:42 PM Page 423 Part 2 sets up some variables that we will use to actually draw the graph. Working out the values for these sorts of variables can be tedious, but a bit of forethought about what you want the finished image to look like will make the drawing process much eas- ier. The values we use here were arrived at by sketching the desired effect on a piece of paper and estimating the required proportions. The $width variable is the total width of the canvas we will use. We also set up the left and right margins (with $left_margin and $right_margin, respectively); the “fatness” and spacing between the bars ($bar_height and $bar_spacing); and the font, font sizes, and label position ($font, $title_size, $main_size, $small_size, and $text_indent). Given these base values, we can then make a few calculations. We want to draw a baseline that all the bars stretch out from. We can work out the position for this baseline by using the left margin plus an allowance for the text labels for the X coordinate, and again an estimate from our sketch for the Y coordinate. We also work out two important values: first, the distance on the graph that represents one unit: $bar_unit = ($width-($x+$right_margin)) / 100; // one “point” on the graph This is the maximum length of the bars—from the baseline to the right margin—divided by 100 because our graph is going to show percentage values. The second value is the total height that we need for the canvas: $height = $num_candidates * ($bar_height + $bar_spacing) + 50; This is basically the height per bar times the number of bars, plus an extra amount for the title. Part 3 is shown in Listing 19.5.3. LISTING 19.5.3 showpoll.php—Part 3 Sets Up the Graph, Ready for the Data to Be Added /******************************************* Set up base image *******************************************/ // create a blank canvas $im = imagecreate($width,$height); // Allocate colors $white=ImageColorAllocate($im,255,255,255); $blue=ImageColorAllocate($im,0,64,128); $black=ImageColorAllocate($im,0,0,0); $pink = ImageColorAllocate($im,255,78,243); Advanced PHP Techniques P ART IV 424 24 7842 CH19 3/6/01 3:42 PM Page 424 . corner 3 Y coordinate, lower-right corner 4 X coordinate, upper-right corner 5 Y coordinate, upper-right corner 6 X coordinate, upper-left corner 7 Y coordinate, upper-left corner To remember what. the login of any user with the appropriate MySQL privileges. LISTING 19.3 pollsetup.sql —Setting Up the Poll Database create database poll; use poll; create table poll_results ( candidate varchar(30), num_votes. minutes to set up, and you can do this by running the SQL script shown in Listing 19.3. You can do this piping the script through a root login using mysql -u root -p < pollsetup.sql Of course,