<?php 

/*
 * PHP GD wrapper functions to provide a better interface to generating images.
 * The Y coordinates for each function are reversed, i.e. 0 is bottom instead of the top as is normally in GD
 * Some of the functions also provide enhanced facilities, e.g. in PG_ImageString you can specify 'center' as an
 * X or Y coordinate (or 'top', 'left', etc)
 *
 * (c) Paul Gregg, <pgregg@pgregg.com> January 2002.
 */

$DEBUG FALSE;


// Allocate some colors
// Note - do not use hex codes as the index (e.g. ['#ffffff']) as these are
// reserved for dynamic color allocation

$PG_ImageColorDef              = array();
$PG_ImageColorDef['white']     = "#ffffff";
$PG_ImageColorDef['black']     = "#000000";
$PG_ImageColorDef['pure_red']  = "#ff0000";
$PG_ImageColorDef['pure_green']= "#00ff00";
$PG_ImageColorDef['pure_blue'] = "#0000ff";

$PG_ImageColorDef['red']       = "255,100,100";
$PG_ImageColorDef['green']     = "100,255,100";
$PG_ImageColorDef['midblue']   = "150,150,255";
$PG_ImageColorDef['blue']      = "100,100,255";
$PG_ImageColorDef['lightgrey'] = "220,220,220";
$PG_ImageColorDef['grey']      = "200,200,200";
$PG_ImageColorDef['midgrey']   = "150,150,150";
$PG_ImageColorDef['darkgrey']  = "100,100,100";
$PG_ImageColorDef['yellow']    = "255,255,0";

$PG_ImageColor = array();      // This is where the above colors are converted into imagecolors which can be used

$PG_LoadedFonts = array();



Function 
PG_ImageCreate(&$image$x$y$bt=0$bb=0$bl=0$br=0) {
    
$image imagecreate($x+$bl+$br$y+$bt+$bb);
}






Function 
PG_ImageColorAllocate(&$image$color) {
    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor;
    if (
substr($color01) == '#'$PG_ImageColorDef["$color"] = $color// dynamically allow #codes
    
if (isset($PG_ImageColorDef["$color"])) {
        if (
substr($PG_ImageColorDef["$color"], 01) == '#') {
            
// This is a HEX color e.g. #ffffff
            
$r hexdec(substr($PG_ImageColorDef["$color"], 12));
            
$g hexdec(substr($PG_ImageColorDef["$color"], 32));
            
$b hexdec(substr($PG_ImageColorDef["$color"], 52));
        } else {
            
// This is a r,g,b reference
            
list ($r,$g,$b) = explode(","$PG_ImageColorDef["$color"]);
        }
        
$PG_ImageColor["$color"] = ImageColorAllocate($image$r$g$b);
        if (
$DEBUG) print "In function PG_ImageColorAllocate(): Setting PG_ImageColor['$color'] to '$r, $g, $b'<br>\n";
    } else
        if (
$DEBUG) print "In function PG_ImageColorAllocate(): Ignoring color set for '$color' - not defined.<br>\n";
}




// Function to draw a line from x1,y1 to x2,y2 in the color col.
// Zero point for x and y coordinates are bottom left and all points will be offset by:
// bl - border left, br - border right, bt - border top, bb - border bottom.
Function PG_ImageLine (&$image$x1$y1$x2$y2$col='black'$bt=0$bb=0$bl=0$br=0$relmaxy=0) {
    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor;
    if (!isset(
$PG_ImageColor["$col"])) {
        
PG_ImageColorAllocate($image$col);
        if (
$DEBUG) print "In function PG_ImageLine(): Calling PG_ImageColorAllocate() with color $col<br>\n";
    }
    
$imagemaxy imagesy($image); // $imagemaxy - $bt - $bb = drawable window for lines in y coord
    
$imagemaxx imagesx($image); // $imagemaxx - $bl - $br = drawable window for lines in x coord
    
$maxy $imagemaxy $bt $bb;
    
$maxx $imagemaxx $bl $br;
            
    
// if $relmaxy is set and > 0 then the y coords need to be modified to be relative to this
    
if ($relmaxy 0) {
        
$y1 $maxy * ($y1 $relmaxy);
        
$y2 $maxy * ($y2 $relmaxy);
    }

    
// Now calculate _real_ X,Y coords by reversing the Y, and offsetting X
    
$X1 $x1 $bl;
    
$Y1 = ($maxy $y1) + $bt;
    
$X2 $x2 $bl;
    
$Y2 = ($maxy $y2) + $bt;

    
// Sanity check x and y coords
    
if ($X1 0$X1 0;
    if (
$Y1 0$Y1 0;
    if (
$X2 0$X2 0;
    if (
$Y2 0$Y2 0;
    if (
$X1 $imagemaxx$X1 $imagemaxx;
    if (
$Y1 $imagemaxy$Y1 $imagemaxy;
    if (
$X2 $imagemaxx$X2 $imagemaxx;
    if (
$Y2 $imagemaxy$Y2 $imagemaxy;

    
    
// Now draw the line
    
ImageLine($image$X1$Y1$X2$Y2$PG_ImageColor["$col"]);
}



// Base code for rectangles.  ImageRectangle() and ImageFilledRectangle() both call this case function
Function PG_ImageBaseRectangle($basetype, &$image$x1$y1$x2$y2$col='black'$bt=0$bb=0$bl=0$br=0) {
    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor;
    if (!isset(
$PG_ImageColor["$col"])) {
        
PG_ImageColorAllocate($image$col);
        if (
$DEBUG) print "In function PG_ImageFilledRectangle(): Calling PG_ImageColorAllocate() with color $col<br>\n";
    }
    
$imagemaxy imagesy($image); // $imagemaxy - $bt - $bb = drawable window for lines in y coord
    
$imagemaxx imagesx($image); // $imagemaxx - $bl - $br = drawable window for lines in x coord
    
$maxy $imagemaxy $bt $bb;
    
$maxx $imagemaxx $bl $br;
    
    
// Now calculate _real_ X,Y coords by reversing the Y, and offsetting X
    
$X1 $x1 $bl;
    
$Y1 = ($maxy $y1) + $bt;
    
$X2 $x2 $bl;
    
$Y2 = ($maxy $y2) + $bt;

    
// Sanity check x and y coords
    
if ($X1 0$X1 0;
    if (
$Y1 0$Y1 0;
    if (
$X2 0$X2 0;
    if (
$Y2 0$Y2 0;
    if (
$X1 $imagemaxx$X1 $imagemaxx;
    if (
$Y1 $imagemaxy$Y1 $imagemaxy;
    if (
$X2 $imagemaxx$X2 $imagemaxx;
    if (
$Y2 $imagemaxy$Y2 $imagemaxy;

    
// Ensure that X1 < X2 and Y1 < Y2 - wierd bug? in ImageFilledRectangle that stops it drawing if not!
    // thankfully we can just transpose them and still retain the valid rectangle
    
if ($X1 $X2) { $tmp $X1$X1 $X2$X2 $tmp; }
    if (
$Y1 $Y2) { $tmp $Y1$Y1 $Y2$Y2 $tmp; }

    if (
$DEBUG) print "In function PG_ImageBaseRectangle($x1, $y1, $x2, $y2): Calling ImageFilledRectangle($X1, $Y1, $X2, $Y2) with color $col<br>\n";

    if (
$basetype == 'filled')
        
ImageFilledRectangle($image$X1$Y1$X2$Y2$PG_ImageColor["$col"]);
    else
        
ImageRectangle($image$X1$Y1$X2$Y2$PG_ImageColor["$col"]);
}

Function 
PG_ImageFilledRectangle(&$image$x1$y1$x2$y2$col='black'$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseRectangle('filled'$image$x1$y1$x2$y2$col$bt$bb$bl$br);
}

Function 
PG_ImageRectangle(&$image$x1$y1$x2$y2$col='black'$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseRectangle('normal'$image$x1$y1$x2$y2$col$bt$bb$bl$br);
}





// Base code for ellipses/circles.  ImageEllipse() and ImageFilledEllipse() both call this case function
Function PG_ImageBaseEllipse($basetype, &$image$x1$y1$w1$h1$col='black'$bt=0$bb=0$bl=0$br=0) {
    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor;
    if (!isset(
$PG_ImageColor["$col"])) {
        
PG_ImageColorAllocate($image$col);
        if (
$DEBUG) print "In function PG_ImageBaseEllipse(): Calling PG_ImageColorAllocate() with color $col<br>\n";
    }
    
$imagemaxy imagesy($image); // $imagemaxy - $bt - $bb = drawable window for lines in y coord
    
$imagemaxx imagesx($image); // $imagemaxx - $bl - $br = drawable window for lines in x coord
    
$maxy $imagemaxy $bt $bb;
    
$maxx $imagemaxx $bl $br;
    
    
// Now calculate _real_ X,Y coords by reversing the Y, and offsetting X
    
$X1 $x1 $bl;
    
$Y1 = ($maxy $y1) + $bt;
    
#$X2 = $x2 + $bl;
    #$Y2 = ($maxy - $y2) + $bt;

    // Sanity check x and y coords
    
if ($X1 0$X1 0;
    if (
$Y1 0$Y1 0;
    
#if ($X2 < 0) $X2 = 0;
    #if ($Y2 < 0) $Y2 = 0;
    
if ($X1 $imagemaxx$X1 $imagemaxx;
    if (
$Y1 $imagemaxy$Y1 $imagemaxy;
    
#if ($X2 > $imagemaxx) $X2 = $imagemaxx;
    #if ($Y2 > $imagemaxy) $Y2 = $imagemaxy;

    /*
    // Ensure that X1 < X2 and Y1 < Y2 - wierd bug? in ImageFilledRectangle that stops it drawing if not!
    // thankfully we can just transpose them and still retain the valid rectangle
    if ($X1 > $X2) { $tmp = $X1; $X1 = $X2; $X2 = $tmp; }
    if ($Y1 > $Y2) { $tmp = $Y1; $Y1 = $Y2; $Y2 = $tmp; }
    */

    
if ($DEBUG) print "In function PG_ImageBaseEllipse($x1, $y1, $w1, $h1): Calling ImageFilledEllipse($X1, $Y1, $w1, $h1) with color $col<br>\n";

    if (
$basetype == 'filled')
        
ImageFilledEllipse($image$X1$Y1$w1$h1$PG_ImageColor["$col"]);
    else
        
ImageEllipse($image$X1$Y1$w1$h1$PG_ImageColor["$col"]);
}

Function 
PG_ImageFilledEllipse(&$image$x1$y1$w1$h1$col='black'$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseEllipse('filled'$image$x1$y1$w1$h1$col$bt$bb$bl$br);
}

Function 
PG_ImageEllipse(&$image$x1$y1$w1$h1$col='black'$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseEllipse('normal'$image$x1$y1$w1$h1$col$bt$bb$bl$br);
}










// Draw a string on the image.  Same arguments as ImageString() except that the x and y can take
// strings 'center', 'left', 'right', 'top' and 'bottom'
// the borders are also factored in in any positioning.
Function PG_ImageBaseString($direction, &$image$font$x$y$str$col$bt=0$bb=0$bl=0$br=0) {

    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor;
    if (!isset(
$PG_ImageColor["$col"])) {
        
PG_ImageColorAllocate($image$col);
        if (
$DEBUG) print "In function PG_ImageLine(): Calling PG_ImageColorAllocate() with color $col<br>\n";
    }
    
$imagemaxy imagesy($image); // $imagemaxy - $bt - $bb = drawable window for lines in y coord
    
$imagemaxx imagesx($image); // $imagemaxx - $bl - $br = drawable window for lines in x coord
    #$maxy = $imagemaxy - $bt - $bb;
    #$maxx = $imagemaxx - $bl - $br;

    
$fontwidth ImageFontWidth($font);
    
$fontheight ImageFontHeight($font);
    
$strwidth $fontwidth strlen($str);

    if (
gettype($x) == 'string') { // we need to calculate the position of x
        
switch($x) {
            case 
'center':
            case 
'centre':
                
$X = ($direction == 'normal') ? ($imagemaxx $strwidth) / : ($imagemaxx $fontheight) / 2;
                break;
            case 
'left':
                
$X $bl;
                break;
            case 
'right':
                
$X = ($direction == 'normal') ? $imagemaxx $strwidth $br : ($imagemaxx $fontheight) - 1;
                break;
            default:
                
$X 0;
        }
    } else {
        
$X $x $bl;
    }
    
    if (
gettype($y) == 'string') { // we need to calculate the position of y
        
switch($y) {
            case 
'center':
            case 
'centre':
                
$Y = ($direction == 'normal') ? ($imagemaxy $fontheight) / : ($imagemaxy/$strwidth/);
                break;
            case 
'top':
                
$Y $bt $bt 1;
                break;
            case 
'bottom':
                
$Y $bb $imagemaxy $fontheight $bb $imagemaxy $fontheight $bb 1;
                break;
            default:
                
$Y $imagemaxy $fontheight;
        }
    } else {
        
$Y $imagemaxy $y $bb;
    }
    
    
// Draw the string text to the image
    
if ($direction == 'normal')
      
ImageString($image$font$X$Y$str$PG_ImageColor["$col"]);
    else
        
ImageStringUp($image$font$X$Y$str$PG_ImageColor["$col"]);
    
}


Function 
PG_ImageString(&$image$font$x$y$str$col$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseString('normal'$image$font$x$y$str$col$bt$bb$bl$br);
}

Function 
PG_ImageStringUp(&$image$font$x$y$str$col$bt=0$bb=0$bl=0$br=0) {
    
PG_ImageBaseString('up'$image$font$x$y$str$col$bt$bb$bl$br);
}





// Draw Legend takes an associative array of 'color' => 'Text' and generates a legend on the image
// x,y pixel coords are the top left of the image
Function PG_DrawLegend(&$image$x$y$font$Legends$bordercolor$backgroundcolor$noline$bt=0$bb=0$bl=0$br=0) {
    global 
$DEBUG$PG_ImageColorDef$PG_ImageColor$PG_LoadedFonts;

    
$imagemaxy imagesy($image); // $imagemaxy - $bt - $bb = drawable window for lines in y coord
    
$imagemaxx imagesx($image); // $imagemaxx - $bl - $br = drawable window for lines in x coord

    // ensure that the colors are available
    
if ($bordercolor!='' && !isset($PG_ImageColor["$bordercolor"])) {
        
PG_ImageColorAllocate($image$bordercolor);
        if (
$DEBUG) print "In function PG_ImageLine(): Calling PG_ImageColorAllocate() with color $bordercolor<br>\n";
    }
    if (
$backgroundcolor!='' && !isset($PG_ImageColor["$backgroundcolor"])) {
        
PG_ImageColorAllocate($image$backgroundcolor);
        if (
$DEBUG) print "In function PG_ImageLine(): Calling PG_ImageColorAllocate() with color $backgroundcolor<br>\n";
    }
    
    
// Check if we can use a TTF font if asked for
    
if (!is_numeric($font)) {  // Check we have the font and that it is loaded
        
list ($fontname$fontheight) = split("_"$font);
        if(!isset(
$PG_LoadedFont["$fontname"])) {
            
$fontpath sprintf("%s/%s.ttf"$_ENV["SystemRoot"], $fontname);
            if (!
file_exists($fontpath))
                
$font 2// revert to standard font
        
}
    }

    
// work out the longest text string    
    
$maxtextlen 0;
    
$maxtextstr "";
    foreach (
$Legends as $color => $text) {
        
$textw strlen($text);
        if (
$textw $maxtextlen) {
            
$maxtextlen $textw;
            
$maxtextstr $text;
        }
    }

    if (
is_numeric($font)) {
        
$fontwidth ImageFontWidth($font);
        
$fontheight ImageFontHeight($font);

        
$LegendW $maxtextlen $fontwidth 20 - ($noline*10);
        
$LegendH count($Legends) * ($fontheight+2) + 4;

    } else {
        
        
$ttfsizes imagettfbbox($fontheight0$fontname$maxtextstr);
        
$LegendW $ttfsizes[2] - $ttfsizes[0] + 20 - ($noline*10);
        
$LegendH count($Legends) * ($fontheight+2) + 4;

    }

    
$LegendX1 $x;
    
$LegendY1 $y ;
    
$LegendX2 $LegendX1 $LegendW;
    
$LegendY2 $LegendY1 $LegendH;

    if (
$backgroundcolor != '')
        
PG_ImageFilledRectangle($image$LegendX1$LegendY1$LegendX2$LegendY2$backgroundcolor$bt$bb$bl$br);
    if (
$bordercolor != '')
        
PG_ImageRectangle($image$LegendX1$LegendY1$LegendX2$LegendY2$bordercolor$bt$bb$bl$br);
    
$legend_count 0;
    foreach (
$Legends as $color => $text) {
        
//$y_coord = $LegendY1 - ($legend_count * 10) + 17;
        
$y_coord $LegendY1 - ($legend_count * ($fontheight+2)) - 2;
        
#PG_ImageLine($image, $LegendX1 + 3, $y_coord - ($fontheight/2), $LegendX1 + 13, $y_coord - ($fontheight/2), $color, 0, 0, $bl);
        
if ($noline != 1)
            
PG_ImageFilledRectangle($image$LegendX1 3$y_coord - ($fontheight/2), $LegendX1 13$y_coord - ($fontheight/2) + intval($fontheight/5) -1$color$bt$bb$bl$br);
        
#if (is_numeric($font))
            
PG_ImageString($image$font$LegendX1 16 - ($noline*10), $y_coord$text$color$bt$bb$bl$br);
        
#else
        #    PG_ImageStringTTF($image, $fontheight, 0, $LegendX1 + 16, $y_coord, $text, $color, $fontname);
        
$legend_count++;
    }
}
?>