Wednesday, April 13, 2011

Generating Graphics in PHP

Chapter 25. Generating Graphics

Topics in This Chapter
·         Dynamic Buttons
·         Generating Graphs on the Fly
·         Bar Graphs
·         Pie Charts
·         Stretching Single-Pixel Images
This chapter explores generating graphics using the GD extension functions described in Chapter 16. It is important to be aware of the issues involved with the creation of graphics on the fly. The first is that it is relatively costly in terms of CPU time. In most cases the flexibility of dynamic graphics is not worth what you pay in the load imposed on the server. Another issue is that making nice-looking graphics from PHP functions is not easy. Many techniques available in graphics editors are next to impossible. As you will see in the examples that follow, a lot of work goes into creating simple, flat charts. Last, while there is adequate support for text, functions you'd expect in a word processor do not exist. Text does not wrap at line ends. There is no concept of leading, spacing, or descenders. Regardless, generating graphics makes sense in some situations. This chapter contains some real examples that you can start using with very little modification.

 

25.1 Dynamic Buttons

Images wrapped in anchor tags are a common navigational device. Instead of plaintext, this method allows you to create buttons similar to those created in the operating system or even to create fanciful icons. In most cases it is best to leave these as graphics created in your favorite graphics editor, because the time between changes is relatively long. However, if you have a button that changes often, it may make sense to create it dynamically with PHP. The content of the button, the label, needs to be available as a string in PHP. It could be a statement setting the value of a variable. It could also be a value retrieved from a file or a database.
An illustration will make this idea clear. Many corporate Web sites have a section for press releases. Instead of just a list of text links, your client wants a graphic of a flaming newspaper for each press release, all under the title "Hot off the Press." Each burning newspaper has text over the top with the headline from the press release. With a small company that issues only one press release a month, you are better off creating these graphics by hand. With a company that issues a press release each week, it starts to make sense to automate the process. You can put the press releases into a database and generate a graphic on the fly as surfers view the list of press releases. One advantage of this approach is that if the CEO finds out you're putting flaming newspapers on the site, you can make a minor modification and the graphics become the company logo with the press-release title over it.
Seriously, you must consider the tradeoffs associated with dynamically created graphics. You don't want to save yourself 15 minutes a month if it makes every page download 30 seconds longer. If you've been working with the Web for any time at all, you know to reuse graphics throughout the site because the browser caches them. The first page may take longer to load, but each successive page 0is faster because the graphics are already loaded in the browser. Dynamic graphics can be cached, of course, but the browser uses the URL to cache files. The GET-method form variables are part of the URL, so http://www.site.com/button.php?label=home&from=1 and http://www.site.com/button.php?label=home&from=2 may create two identical graphics but are different as far as the browser cache is concerned.
These are only some of the issues involved with dynamic buttons. To demonstrate the process, I'll provide an example and describe the steps. Listing 25.1 is a script that creates a PNG image of a button with a text label. The button is rectangular and has some highlighting and shadowing. The label has a drop-shadow effect applied to it and is centered both vertically and horizontally. The output is shown in Figure 25.1.
Listing 25.1 PNG button
<?php
    /*
    ** PNG button
    ** Creates a graphical button based
    ** on form variables.
    */
 
    class Button
    {
        private $image;
        public function __construct($width, $height, $label, $font)
        {
            $this->image = imagecreate($width, $height);
            $colorBody = imagecolorallocate($this->image,
                0x99, 0x99, 0x99);
            $colorShadow = imagecolorallocate($this->image,
                0x33, 0x33, 0x33);
            $colorHighlight = imagecolorallocate($this->image,
                0xCC, 0xCC, 0xCC);
 
            //create body of button
            imagefilledrectangle($this->image,
                1, 1, $width-2, $height-2,
                $colorBody);
 
            //draw bottom shadow
            imageline($this->image,
                0, $height-1,
                $width-1, $height-1,
                $colorShadow);
 
            //draw right shadow
            imageline($this->image,
                $width-1, 1,
                $width-1, $height-1,
                $colorShadow);
 
            //draw top highlight
            imageline($this->image,
                0, 0,
                $width-1, 0,
                $colorHighlight);
 
            //draw left highlight
            imageline($this->image,
                0, 0,
                0, $height-2,
                $colorHighlight);
 
            //determine label size
            $labelHeight = imagefontheight($font);
            $labelWidth = imagefontwidth($font) * strlen($label);
 
            //determine label upper left corner
            $labelX = ($width - $labelWidth)/2;
            $labelY = ($height - $labelHeight)/2;
 
            //draw label shadow
            imagestring($this->image,
                $font,
                $labelX+1,
                $labelY+1,
                $label,
                $colorShadow);
 
            //draw label
            imagestring($this->image,
                $font,
                $labelX,
                $labelY,
                $label,
                $colorHighlight);
        }
 
        public function drawPNG()
        {
            header("Content-type: image/png");
            imagepng($this->image);
        }
 
        public function drawJPEG()
        {
            header("Content-type: image/jpeg");
            imagejpeg($this->image);
        }
    }
 
    //set parameters if not given
    if(!isset($_REQUEST['width']))
    {
        $_REQUEST['width'] = 100;
    }
 
    if(!isset($_REQUEST['height']))
    {
        $_REQUEST['height'] = 30;
    }
 
    if(!isset($_REQUEST['label']))
    {
        $_REQUEST['label'] = "CLICK";
    }
 
    if(!isset($_REQUEST['font']))
    {
        $_REQUEST['font'] = 5;
    }
 
    $b = new Button($_REQUEST['width'], $_REQUEST['height'],
        $_REQUEST['label'], $_REQUEST['font']);
    $b->drawPNG();
?>
Figure 25.1. Output from Listing 25.1.
graphics/25fig01.gif
The first step the script takes is to make sure it has valid information for all the parameters. These include the size of the button and the text with which to label the button. I've chosen to use the built-in fonts, which are numbered one through five. Chapter 16 has descriptions of functions for loading different fonts, and I encourage you to modify my script to incorporate them.
The next step is to create an image. There are two ways to do this. You can create a blank image of a specific size, or you can load an existing image file. I've chosen the former because it allows the script to make buttons of any size. You can make much more stylish buttons using the latter method. This is another good exercise.
The button will be drawn with three colors: a body color, a highlight color, and a shadow color. I've chosen to go with three shades of gray. These colors must be allocated with the imagecolorallocate function. Using the body color, the script makes a rectangle that is one pixel smaller than the entire image. The border around this rectangle is created with four lines. The lines on the bottom and right sides are drawn in the shadow color, and the top and left sides are drawn with the highlight color. This creates an illusion of the button being three-dimensional.
To finish the button, the script draws the label. First, the text is drawn slightly off center in the shadow color. Then the text is drawn in the highlight color over it and exactly centered, making the text look as though it is floating over the button.
At this point the script has created the image and needs to send it to the browser. It is very important that the header be sent to let the browser know that this file is an image. Without it, you get a garbled bunch of strange characters.
This wraps up the script that creates a button, but to really make use of it, we have to use it in the context of a Web page. Listing 25.2 demonstrates the minimal steps. I've created an array of four button labels I want to create. I then loop through the array, each time creating an image tag. The source of the image is the previous script. I pass the script some parameters to set the size of the button and the label. I leave the font as the default, but I could have set that as well. The output is shown in Figure 25.2.
Listing 25.2 Creating buttons dynamically
<?php
    //define button labels
    $label = array("HOME",
        "ABOUT US",
        "OUR PRODUCTS",
        "CONTACT US");
 
    //display all buttons
    foreach($label as $text)
    {
        //link back to this page
        print("<a href=\"$SERVER['PHP_SELF']}\">");
 
        //create dynamic image tag
        print("<img src=\"25-1.php");
        print("?label=" . htmlentities($text));
        print("&amp;width=145");
        print("&amp;height=25");
        print("\" border=\"0\"");
        print("width=\"145\" height=\"25\">");
 
        print("</a><br>\n");
    }
?>
 

25.2 Generating Graphs on the Fly

Perhaps a more likely use of dynamic graphics is in generating graphs. Since graphs rely on data, they lend themselves to formula-driven creation. If the data change often, using PHP to generate the graphs is a good idea. In the following examples, I've written the data into the script, but pulling data from a database is not difficult. Sending the data from a form is probably not a practical idea for large amounts of data. The GET method imposes a relatively small limit on the total size of a URL that varies between Web servers. You could use the POST method, however. The two examples I'll show are a bar graph and a pie chart. Each uses the same set of data, which is a fictitious survey of favorite meat.

 

25.3 Bar Graphs

Bar graphs are a good way to compare values to each other. Creating them is a relatively simple task because each data point is a rectangle. The height of the rectangle represents the value of the data point. To make the transition, a scaling factor is used. In Listing 25.3 the graph is 200 pixels tall and the scaling factor is two. This means that a data point with the value 75 will be 150 pixels tall. The output is shown in Figure 25.3.
Listing 25.3 Creating a bar graph
<?php
    /*
    ** Bar graph
    */
 
    //fill in graph parameters
    $GraphWidth = 400;
    $GraphHeight = 200;
    $GraphScale = 2;
    $GraphFont = 5;
    $GraphData = array(
        "Beef"=>"99",
        "Pork"=>"75",
        "Chicken"=>"15",
        "Lamb"=>"66",
        "Fish"=>"22");
 
    //create image
    $image = imagecreate($GraphWidth, $GraphHeight);
    imageantialias($image, TRUE);
 
    //allocate colors
    $colorBody = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
    $colorGrid = imagecolorallocate($image, 0xCC, 0xCC, 0xCC);
    $colorBar = imagecolorallocate($image, 0xFF, 0xFF, 0x00);
    $colorText = imagecolorallocate($image, 0x00, 0x00, 0x00);
 
    //fill background
    imagefill($image, 0, 0, $colorBody);
 
    //draw vertical grid line
    $GridLabelWidth = imagefontwidth($GraphFont)*3 + 1;
    imageline($image,
        $GridLabelWidth, 0,
        $GridLabelWidth, $GraphHeight-1,
        $colorGrid);
 
    //draw horizontal grid lines
    $styleDashed = array_merge(array_fill(0, 4, $colorGrid),
        array_fill(0, 4, IMG_COLOR_TRANSPARENT));
    imagesetstyle($image, $styleDashed);
    for($index = 0;
        $index < $GraphHeight;
        $index += $GraphHeight/10)
    {
        imageline($image,
            0, $index,
            $GraphWidth-1, $index,
            IMG_COLOR_STYLED);
 
        //draw label
        imagestring($image,
            $GraphFont,
            0,
            $index,
            round(($GraphHeight - $index)/$GraphScale),
            $colorText);
    }
 
    //add bottom line
    imageline($image,
        0, $GraphHeight-1,
        $GraphWidth-1, $GraphHeight-1,
        $colorGrid);
 
    //draw each bar
    $BarWidth = (($GraphWidth-$GridLabelWidth)/count($GraphData))
        - 10;
    $column = 0;
    foreach($GraphData as $label=>$value)
    {
        //draw bar
        $BarTopX = $GridLabelWidth +
            (($column+1) * 10) + ($column * $BarWidth);
        $BarBottomX = $BarTopX + $BarWidth;
        $BarBottomY = $GraphHeight-1;
        $BarTopY = $BarBottomY - ($value * $GraphScale);
 
        imagefilledrectangle($image,
            $BarTopX, $BarTopY,
            $BarBottomX, $BarBottomY,
            $colorBar);
 
        //draw label
        $LabelX = $BarTopX +
            (($BarBottomX - $BarTopX)/2) -
            (imagefontheight($GraphFont)/2);
        $LabelY = $BarBottomY-10;
 
        imagestringup($image,
            $GraphFont,
            $LabelX,
            $LabelY,
            "$label: $value",
            $colorText);
 
        $column++;
    }
 
    //output image
    header("Content-type: image/png");
    imagepng($image);
?>

 

 

25.4 Pie Charts

Pie charts are a good way to see how a value represents a percentage of a whole. Each data point is a slice of a pie with a unique color. A legend associates the colors with each data point's label and value.
Since the pie chart is round, it represents a slightly more complex problem than the bar graph. PHP's image functions allow you to draw a pie slice, solid or outlined. Because each slice represents a portion of the whole, the script must calculate how many degrees to dedicate to the slice by dividing the value by the total of all slice values. Then it's a matter of calling imagefilledarc.
As with the bar graph, the data used in the chart come from an array hardcoded into the script in Listing 25.4. It is possible to keep the chart up to date by editing every time the data change, but it may be better to link it with a database. The output is shown in Figure 25.4.
Listing 25.4 Creating a pie chart
<?php
    //fill in chart parameters
    $ChartDiameter = 300;
    $ChartFont = 5;
    $ChartFontHeight = imagefontheight($ChartFont);
    $ChartData = array(
        "Beef"=>"99",
        "Pork"=>"75",
        "Chicken"=>"15",
        "Lamb"=>"66",
        "Fish"=>"22");
 
    //determine graphic size
    $ChartWidth = $ChartDiameter + 20;
    $ChartHeight = $ChartDiameter + 20 +
        (($ChartFontHeight + 2) * count($ChartData));
 
    //determine total of all values
    $ChartTotal = array_sum($ChartData);
 
    //set center of pie
    $ChartCenterX = $ChartDiameter/2 + 10;
    $ChartCenterY = $ChartDiameter/2 + 10;
 
    //create image
    $image = imagecreate($ChartWidth, $ChartHeight);
    imageantialias($image, TRUE);
 
    //create a round brush for drawing borders
    $dot = imagecreate(10, 10);
    $dotColorBlack = imagecolorallocate($dot, 0, 0, 0);
    $dotColorTransparent = imagecolorallocate($dot, 255, 0, 255);
    imagecolortransparent($dot, $dotColorTransparent);
    imagefill($dot, 0, 0, $dotColorTransparent);
    imagefilledellipse($dot, 4, 4, 5, 5, $dotColorBlack);
    imagesetbrush($image, $dot);
 
 
    //allocate colors
    $colorBody = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
    $colorBorder = imagecolorallocate($image, 0x00, 0x00, 0x00);
    $colorText = imagecolorallocate($image, 0x00, 0x00, 0x00);
    $colorSlice = array(
 
        imagecolorallocate($image, 0xFF, 0x00, 0x00),
        imagecolorallocate($image, 0x00, 0xFF, 0x00),
        imagecolorallocate($image, 0x00, 0x00, 0xFF),
        imagecolorallocate($image, 0xFF, 0xFF, 0x00),
        imagecolorallocate($image, 0xFF, 0x00, 0xFF),
        imagecolorallocate($image, 0x00, 0xFF, 0xFF),
        imagecolorallocate($image, 0x99, 0x00, 0x00),
        imagecolorallocate($image, 0x00, 0x99, 0x00),
        imagecolorallocate($image, 0x00, 0x00, 0x99),
        imagecolorallocate($image, 0x99, 0x99, 0x00),
        imagecolorallocate($image, 0x99, 0x00, 0x99),
        imagecolorallocate($image, 0x00, 0x99, 0x99));
 
    //fill background
    imagefill($image, 0, 0, $colorBody);
 
 
    /*
    ** draw each slice
    */
    $Degrees = 0;
    $slice=0;
    foreach($ChartData as $label=>$value)
    {
        $StartDegrees = round($Degrees);
        $Degrees += (($value/$ChartTotal)*360);
        $EndDegrees = round($Degrees);
 
        $CurrentColor = $colorSlice[$slice%(count($colorSlice))];
 
        //draw pie slice
        imagefilledarc(
            $image,
            $ChartCenterX, $ChartCenterY,
            $ChartDiameter,$ChartDiameter,
            $StartDegrees, $EndDegrees,
            $CurrentColor, IMG_ARC_PIE);
 
 
        //draw legend for this slice
        $LineY = $ChartDiameter + 20 +
            ($slice*($ChartFontHeight+2));
 
        imagerectangle($image,
            10,
            $LineY,
            10 + $ChartFontHeight,
            $LineY+$ChartFontHeight,
            $colorBorder);
 
        imagefilltoborder($image,
            12,
            $LineY + 2,
            $colorBorder,
            $CurrentColor);
 
        imagestring($image,
            $ChartFont,
            20 + $ChartFontHeight,
            $LineY,
            "$label: $value",
            $colorText);
 
        $slice++;
    }
 
 
    //draw border
    imageellipse($image,
        $ChartCenterX, $ChartCenterY,
        $ChartDiameter,$ChartDiameter,
        IMG_COLOR_BRUSHED);
 
    //output image
    header("Content-type: image/png");
    imagepng($image);
?>


25.5 Stretching Single-Pixel Images

The following technique takes advantage of the behavior of most browsers with the width and height properties of the image tag. It does not require the GD extension, because it doesn't actually manipulate an image. It relies on the browser to stretch an image to match the width and height specified in the IMG tag. This allows you to stretch a single-pixel image into a large bar.
Refer to Listing 25.5. An HTML table is used to line up graph labels with bars. The largest data element will fill 100 percent of the graph width, which is specified by the graphWidthMax variable. Each element is pulled from the data array and used to scale graphWidthMax. This produces a horizontally oriented bar graph, but the same method can make a vertical graph too. You may wish to add a second, clear image to the right of each bar to ensure the graph renders correctly on all browsers. See Figure 25.5.
Listing 25.5 Bar graph using stretched images
<?php
    //fill in graph parameters
    $graphWidthMax = 400;
    $graphData = array(
        "Beef"=>"99",
        "Pork"=>"75",
        "Chicken"=>"15",
        "Lamb"=>"66",
        "Fish"=>"22");
    $barHeight = 10;
    $barMax = max($graphData);
 
    print("<table border=\"0\">\n");
 
    foreach($graphData as $label=>$rating)
    {
        //calculate width
        $barWidth = intval($graphWidthMax * $rating/$barMax);
 
        print("<tr>\n");
 
        //label
        print("<th>$label</th>\n");
 
        //data
        print("<td>");
        print("<img src=\"reddot.png\" ");
        print("width=\"$barWidth\"  height=\"$barHeight\" ");
        print("border=\"0\">");
        print("</td>\n");
 
        print("</tr>\n");
    }
 
    print("</table>\n");
?>

25.5 Stretching Single-Pixel Images

The following technique takes advantage of the behavior of most browsers with the width and height properties of the image tag. It does not require the GD extension, because it doesn't actually manipulate an image. It relies on the browser to stretch an image to match the width and height specified in the IMG tag. This allows you to stretch a single-pixel image into a large bar.
Refer to Listing 25.5. An HTML table is used to line up graph labels with bars. The largest data element will fill 100 percent of the graph width, which is specified by the graphWidthMax variable. Each element is pulled from the data array and used to scale graphWidthMax. This produces a horizontally oriented bar graph, but the same method can make a vertical graph too. You may wish to add a second, clear image to the right of each bar to ensure the graph renders correctly on all browsers. See Figure 25.5.
Listing 25.5 Bar graph using stretched images
<?php
    //fill in graph parameters
    $graphWidthMax = 400;
    $graphData = array(
        "Beef"=>"99",
        "Pork"=>"75",
        "Chicken"=>"15",
        "Lamb"=>"66",
        "Fish"=>"22");
    $barHeight = 10;
    $barMax = max($graphData);
 
    print("<table border=\"0\">\n");
 
    foreach($graphData as $label=>$rating)
    {
        //calculate width
        $barWidth = intval($graphWidthMax * $rating/$barMax);
 
        print("<tr>\n");
 
        //label
        print("<th>$label</th>\n");
 
        //data
        print("<td>");
        print("<img src=\"reddot.png\" ");
        print("width=\"$barWidth\"  height=\"$barHeight\" ");
        print("border=\"0\">");
        print("</td>\n");
 
        print("</tr>\n");
    }
 
    print("</table>\n");
?>

By PHP with No comments

Software Engineering in PHP


Part IV: Software Engineering
Software engineering is more than just programming. Like a civil engineer carefully designs and builds a skyscraper, a software engineer carefully designs and implements software systems. Even small PHP scripts may benefit from software engineering concepts. This section explores the issues involved in using PHP in a Web site.
Chapter 26 is about integrating PHP and HTML. You can use PHP just in key places or in generating every page of a site. This chapter helps you decide.
Chapter 27 discusses system specification and design. It develops an approach for designing a system with PHP, including a phase of careful requirements analysis. A survey is made of existing methods for designing with PHP.
Chapter 28 touches on issues of efficiency and debugging. It provides information to help measure performance, and it describes remote debugging.
Chapter 29 discusses implementing design patterns in PHP.

Chapter 26 PHP Integration with HTML- Simple Way to integrate PHP with HTML

 



Chapter 27 PHP Design in Software Engineering PHP Tutorial

 



Chapter 28 PHP Efficiency and Debugging in Software Engineering Model

 



Chapter 29 PHP Design Patterns in Software Engineering Model

 



By PHP with No comments

    • Popular
    • Categories
    • Archives