<?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($color, 0, 1) == '#') $PG_ImageColorDef["$color"] = $color; // dynamically allow #codes
    if (isset($PG_ImageColorDef["$color"])) {
        if (substr($PG_ImageColorDef["$color"], 0, 1) == '#') {
            // This is a HEX color e.g. #ffffff
            $r = hexdec(substr($PG_ImageColorDef["$color"], 1, 2));
            $g = hexdec(substr($PG_ImageColorDef["$color"], 3, 2));
            $b = hexdec(substr($PG_ImageColorDef["$color"], 5, 2));
        } 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) / 2 : ($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) / 2 : ($imagemaxy/2 + $strwidth/2 );
                break;
            case 'top':
                $Y = $bt > 0 ? $bt : 1;
                break;
            case 'bottom':
                $Y = $bb > 0 ? $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($fontheight, 0, $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++;
    }
}
?>