<?php
//////////////////////////////////////////////////////////////
///                                                         //
//  LastXgraph() by James Heinrich <info@silisoftware.com>  //
//        available at http://www.silisoftware.com          //
//                                                         ///
//////////////////////////////////////////////////////////////
///                                                         //
//         This code is released under the GNU GPL:         //
//           http://www.gnu.org/copyleft/gpl.html           //
//                                                          //
// If you do use this code somewhere, send me an email and  //
// tell me how/where you used it.                           //
//                                                         ///
//////////////////////////////////////////////////////////////
///                                                         //
// v1.1.10 - Jan 12, 2004                                   //
//   * Moved demo code out of LastXgraph.php                //
//   * Added GD error message output where available        //
//   * Bugfix: TTF support now disabled if font file does   //
//     not exist                                            //
//                                                          //
// v1.1.9 - Dec 10, 2003                                    //
//   * Fixed alignment problems with TTF text labels        //
//   * Antialiased text with GD2                            //
//   * Bugfix: incorrect month name for pastxdays method    //
//     (thanks kimØsteinhaug*com)                           //
//                                                          //
// v1.1.8 - Sep 28, 2003                                    //
//   * Missing ordinal array keys are filled with zero if   //
//     missing from source data                             //
//                                                          //
// v1.1.7 - Jan 27, 2003                                    //
//   * Support for register_globals==off for demo mode      //
//   * Bugfix: $lastx_max_date was not being set to current //
//     time if undefined                                    //
//                                                          //
// v1.1.6 - Sep 06, 2002                                    //
//   * Bugfix: Left axis was being double-written if TTF    //
//     support not available                                //
//   * added lastx_max_date parameter for lastX (month and  //
//     year) graphs - allows you to specify the max date    //
//     for the graphs, rather than having the max date be   //
//     the current date                                     //
//                                                          //
// v1.1.5 - Aug 12, 2002                                    //
//   * Bugfix: crash if TTF support not compiled into PHP   //
//   * Bugfix: typo in "specificyear" method                //
//   * Better support for register_globals = off            //
//   * Removed some PHP_NOTICE warnings                     //
//   * Scaling font size support when TTF not supported     //
//                                                          //
// v1.1.4 - Jul 01, 2002                                    //
//   * Error messages now written to the image rather than  //
//     as a plain-text message                              //
//   * Bugfix: TTF font file was being checked for even if  //
//     FreeType support not enabled                         //
//                                                          //
// v1.1.3 - May 17, 2002                                    //
//   * Limited width and height to 8192px to guard against  //
//     runaway memory allocation and/or server crashing     //
//     (Apache crashes if either parameter of CreateImage() //
//     is outside the range of int)                         //
//   * added support for graphmethod "specificyear" which   //
//     is similar to "specificmonth", but for a year, with  //
//     the x-axis delimited in months, not days             //
//   * added ImageDestroy() to free up resources            //
//                                                          //
// v1.1.2 - May 15, 2002                                    //
//   * added support for graphmethod "specificmonth" which  //
//     graphs data over an entire calendar month, not just  //
//     the last X days. The format is specificmonthYYYYMM   //
//     where YYYY is the year and MM the month that you     //
//     want to graph. Day 1 of the month is stored in array //
//     element zero, Day 31 of the month is stored in array //
//     element 30, etc
//                                                          //
// v1.1.1 - May 10, 2002                                    //
//   * improved plotting of average curve (line didn't go   //
//     to last data points for some BezierPrecision values  //
//                                                          //
// v1.1.0 - May 8, 2002                                     //
//   * works with built-in fonts if FreeType not supported  //
//   * detects which output format (PNG, GIF or JPEG) is    //
//     supported and outputs in that format (in that order  //
//     of preference)                                       //
//   * added support for graphmethod "pastxmonths" which is //
//     identical to "pastxdays", except the array index     //
//     counts back months rather than days - useful for     //
//     longer-term graphs, spanning years rather than weeks //
//   * auto-fits as many x-axis labels on the graph as can  //
//     fit in the available width                           //
//                                                          //
// v1.0.0 - May 2, 2002                                     //
//   * initial public release                               //
//                                                         ///
//////////////////////////////////////////////////////////////


function DisplayImage(&$im) {
    
$imagetypes imagetypes();
    if (
$imagetypes IMG_PNG) {
        
header ('Content-type: image/png');
        
ImagePNG($im);
    } else if (
$imagetypes IMG_GIF) {
        
header ('Content-type: image/gif');
        
ImageGIF($im);
    } else if (
$imagetypes IMG_JPG) {
        
header ('Content-type: image/jpeg');
        
ImageJPEG($im);
    } else {
        
ErrorImage('No supported format (PNG, GIF, JPG)');
    }
    
ImageDestroy($im);
    return 
true;
}

function 
ImageHexColorAllocate(&$img$HexColorString) {
    
$R hexdec(substr($HexColorString02));
    
$G hexdec(substr($HexColorString22));
    
$B hexdec(substr($HexColorString42));
    return 
ImageColorAllocate($img$R$G$B);
}

function 
ErrorImage($text$width=400$height=100$bgcolor='CCCCFF'$textcolor='FF0000') {
    
$fontsize 1;

    
$LinesOfText explode("\n"wordwrap($textfloor($width ImageFontWidth($fontsize)), "\n"true));
    
$height max($heightcount($LinesOfText) * ImageFontHeight($fontsize));

    if (
$errorimg ImageCreate($width$height)) {

        
$background_color ImageHexColorAllocate($errorimg$bgcolor);
        
$text_color       ImageHexColorAllocate($errorimg$textcolor);

        
ImageFilledRectangle($errorimg00$width$height$background_color);
        
$lineYoffset 0;
        foreach (
$LinesOfText as $line) {
            
ImageString($errorimg$fontsize2$lineYoffset$line$text_color);
            
$lineYoffset += ImageFontHeight($fontsize);
        }
        
$imagetypes imagetypes();
        if (
$imagetypes IMG_PNG) {
            
header('Content-type: image/png');
            
ImagePNG($errorimg);
        } elseif (
$imagetypes IMG_GIF) {
            
header('Content-type: image/gif');
            
ImageGIF($errorimg);
        } elseif (
$imagetypes IMG_JPG) {
            
header('Content-type: image/jpeg');
            
ImageJPEG($errorimg);
        } else {
            echo 
$text;
        }
        
ImageDestroy($errorimg);

    } else {

        echo 
$text;

    }
    
ImageDestroy($errorimg);
    return 
true;
}

function 
DeCasteljau($points$pct) {
    
// http://www.cubic.org/~submissive/sourcerer/bezier.htm
    // http://www.ajzworld.com/Bezier/Bezier.html
    
for ($i 0$i count($points['x']); $i++) {
        
$tempX[$i] = $points['x'][$i];
        
$tempY[$i] = $points['y'][$i];
    }

    for (
$k 1$k count($points['x']); $k++) {
        for (
$i 0$i count($points['x']) - $k$i++) {
            
$tempX[$i] = ((1.0 $pct) * $tempX[$i]) + ($pct $tempX[$i 1]);
            
$tempY[$i] = ((1.0 $pct) * $tempY[$i]) + ($pct $tempY[$i 1]);
        }
    }
    
$calculatedpoint['x'] = $tempX[0];
    
$calculatedpoint['y'] = $tempY[0];

    return 
$calculatedpoint;
}

function 
PointToBuiltInFontSize($fontsize) {
    if (
$fontsize >= 16) {
        return 
5;
    } elseif (
$fontsize >= 14) {
        return 
4;
    } elseif (
$fontsize >= 12) {
        return 
3;
    } elseif (
$fontsize >= 10) {
        return 
2;
    }
    return 
1;
}

function 
LastXgraph($data$graphmethod='24hourdistribution'$title='Graph Title'$scalelabel='axis label'$PlotAverage=true$BezierPrecision=20$width=600$height=300$TTFfontfile='arial.ttf'$fontsize=11$Yaxisdivs=10$col_background='CCCCCC'$col_border='000000'$col_text='FFFFFF'$col_bars='3399FF;FF9933;FF0000;99FF33;FF33FF'$lastx_max_date='') {
    if ((
$width 8192) || ($height 8192) || ($width <= 0) || ($height <= 0)) {
        
ErrorImage('Image size is limited to 8192 x 8192 for safety reasons');
        return 
false;
    }
    if (empty(
$lastx_max_date)) {
        
$lastx_max_date time();
    }
    if (
$im ImageCreate($width$height)) {
        
ImageHexColorAllocate($im$col_background);
        
$background_color ImageHexColorAllocate($im$col_background);
        
$border_color     ImageHexColorAllocate($im$col_border);
        
$text_color       ImageHexColorAllocate($im$col_text);
        
$BuiltInFontSize  PointToBuiltInFontSize($fontsize);
        
$TTFsupport false;
        if (
function_exists('ImageTTFBBox') && is_readable($TTFfontfile) && (@ImageTTFBBox(100$TTFfontfile'test') !== false)) {
            
$TTFsupport true;
        }
        
$PSsupport false;
        if (
function_exists('ImagePSText') && (@ImagePSText($im'test'1$TTFfontfile$text_color$background_color$width 2$height 2))) {
            
$PSsupport true;
        }
        if (
is_array($data)) {
            foreach (
$data as $stringkey => $datarray) {
                foreach (
$datarray as $key => $val) {
                    if (!isset(
$minkey)) {
                        
$minkey $key;
                        
$maxkey $key;
                    }
                    
$minkey min($minkey$key);
                    
$maxkey max($maxkey$key);
                }
                for (
$i $minkey$i <= $maxkey$i++) {
                    if (!isset(
$data[$stringkey][$i])) {
                        
$data[$stringkey][$i] = 0;
                    }
                }
            }

            foreach (
$data as $key => $val) {
                if (!
is_array($val)) {
                    
ErrorImage('$data['.$key.'] is not an array');
                    return 
false;
                }
                if ((
$graphmethod == '24hourdistribution') && (count($val) != 24)) {
                    
$graphmethod 'pastxdays';
                }
            }
        } else {
            
ErrorImage('$data is not an array');
            return 
false;
        }
        if (
$TTFsupport && !is_readable($TTFfontfile)) {
            
ErrorImage('Cannot open font file: '.$TTFfontfile);
            return 
false;
        }
        if ((
$graphmethod != 'pastxdays') && ($graphmethod != '24hourdistribution') && ($graphmethod != 'pastxmonths') && (substr($graphmethod0strlen('specificmonth')) != 'specificmonth') && (substr($graphmethod0strlen('specificyear')) != 'specificyear')) {
            
ErrorImage('$graphmethod ('.$graphmethod.') not supported');
            return 
false;
        }

        
$bar_colorsarray explode(';'$col_bars);
        if (
count($bar_colorsarray) < count($data)) {
            
ErrorImage('$col_bars only has '.count($bar_colorsarray).' colours specified, but there are '.count($data).' data sets');
            return 
false;
        } else {
            for (
$i 0$i count($bar_colorsarray); $i++) {
                
$bar_colors[]  = ImageColorAllocate($imhexdec(substr($bar_colorsarray[$i], 02)), hexdec(substr($bar_colorsarray[$i], 22)), hexdec(substr($bar_colorsarray[$i], 42)));
                
$line_colors[] = ImageColorAllocate($imhexdec(substr($bar_colorsarray[$i], 02))*0.8hexdec(substr($bar_colorsarray[$i], 22))*0.8hexdec(substr($bar_colorsarray[$i], 42))*0.8);
            }
        }

        if (
$TTFsupport) {
            
$bottommargin $fontsize 3;
            
$topmargin    $fontsize 2.5;
        } else {
            
$bottommargin ImageFontHeight($BuiltInFontSize) * 2.5;
            
$topmargin    ImageFontHeight($BuiltInFontSize) * 2;
        }

        
// scale
        
$lastxdays    0;
        
$maxdatarange 0;
        foreach (
$data as $key => $val) {
            
$maxdatarange max($maxdatarange, (max($data[$key])));
            
$lastxdays    max($lastxdayscount($data[$key]));
        }
        
$scalemultiplier 0;
        for (
$tempscale $maxdatarange$tempscale >= 50$scalemultiplier++) { // make left-side scale into even-looking numbers (10000 instead of 9842)
            
$tempscale $tempscale 10;
        }
        
$scale = (ceil($tempscale) * pow(10$scalemultiplier)) / 2;
        if (!
$scale) { // just in case scale happens to fall through and be zero
            
$scale $Yaxisdivs;
        }
        while (
$scale $Yaxisdivs) { // make sure scale is a multiple of the number of axis divisions
            
$scale++;
        }

        
// check width of left margin
        
$leftmargin 0;
        for (
$i $Yaxisdivs$i >= 0$i--) {
            if (
$TTFsupport) {
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfilenumber_format(($i $Yaxisdivs) * $scale));
                
$leftmargin   max($textboundbox[2] + ($fontsize 0.5), $leftmargin); // increase left margin width if not wide enough
            
} else {
                
$leftmargin   max(ImageFontWidth($BuiltInFontSize) * strlen(number_format(($i $Yaxisdivs) * $scale)), $leftmargin); // increase left margin width if not wide enough
            
}
        }
        if (
$TTFsupport) {
            
$leftmargin += ($fontsize 1.5); // scale label
        
} else {
            
$leftmargin += (ImageFontHeight($BuiltInFontSize) * 1.5); // scale label
        
}

        
// border backgrounds
        
ImageFilledRectangle($im00$width$topmargin$border_color);                    // top margin dark background
        
ImageFilledRectangle($im0$height $bottommargin$width$height$border_color); // left margin dark background
        
ImageFilledRectangle($im00$leftmargin$height $bottommargin$border_color);  // bottom margin dark background

        // title
        
if ($TTFsupport) {
            
$textboundbox ImageTTFBBox($fontsize 1.50$TTFfontfile$title);
            
ImageTTFText($im$fontsize 1.50$width $textboundbox[2] - $fontsize$fontsize 2$text_color$TTFfontfile$title);
        } else {
            
ImageString($im$BuiltInFontSize$width - (ImageFontWidth($BuiltInFontSize) * strlen($title)), 0$title$text_color);
        }

        
// left-size scale labels
        
if ($TTFsupport) {
            
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$scalelabel);
            
ImageTTFText($im$fontsize90$fontsize$height - (($height $textboundbox[2]) / 2), $text_color$TTFfontfile$scalelabel);
            for (
$i $Yaxisdivs$i >= 0$i--) {
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfilenumber_format(($i $Yaxisdivs) * $scale));
                
//ImageTTFText($im, $fontsize, 0, $leftmargin - $textboundbox[2] - ($fontsize * 0.3), (($Yaxisdivs - $i) * (($height - $bottommargin - $topmargin) / $Yaxisdivs)) + $topmargin - (ImageFontWidth($BuiltInFontSize) / 2), $text_color, $TTFfontfile, number_format(($i / $Yaxisdivs) * $scale));
                
ImageTTFText($im$fontsize0$leftmargin $textboundbox[2] - ($fontsize 0.3), (($Yaxisdivs $i) * (($height $bottommargin $topmargin) / $Yaxisdivs)) + $topmargin - ($textboundbox[5] / 2), $text_color$TTFfontfilenumber_format(($i $Yaxisdivs) * $scale));
            }
        } else {
            
ImageStringup($im$BuiltInFontSize0$height - (($height - (ImageFontWidth($BuiltInFontSize) * strlen($scalelabel))) / 2), $scalelabel$text_color);
            for (
$i $Yaxisdivs$i >= 0$i--) {
                
ImageString($im$BuiltInFontSize$leftmargin - (ImageFontWidth($BuiltInFontSize) * strlen(number_format(($i $Yaxisdivs) * $scale))), (($Yaxisdivs $i) * (($height $bottommargin $topmargin) / $Yaxisdivs)) + $topmarginnumber_format(($i $Yaxisdivs) * $scale), $text_color);
            }
        }

        
// horizontal lines
        
for ($i $Yaxisdivs$i >= 0$i--) {
            
ImageLine($im$leftmargin, (($Yaxisdivs $i) * (($height $bottommargin $topmargin) / $Yaxisdivs)) + $topmargin$width, (($Yaxisdivs $i)*(($height $bottommargin $topmargin) / $Yaxisdivs)) + $topmargin$border_color);
        }

        if ((
$graphmethod == 'pastxdays') || ($graphmethod == 'pastxmonths') || (substr($graphmethod0strlen('specificmonth')) == 'specificmonth') || (substr($graphmethod0strlen('specificyear')) == 'specificyear')) {
            if (
substr($graphmethod0strlen('specificmonth')) == 'specificmonth') {
                
$specificmonthdate mktime(1200substr($graphmethodstrlen('specificmonth') + 42), 1substr($graphmethodstrlen('specificmonth'), 4));
                
$lastxdays = (int) date('j'mktime(1200substr($graphmethodstrlen('specificmonth') + 42) + 10substr($graphmethodstrlen('specificmonth'), 4)));
            } else if (
substr($graphmethod0strlen('specificyear')) == 'specificyear') {
                
$specificyeardate mktime(120011substr($graphmethodstrlen('specificyear'), 4));
                
$lastxdays 12;
            }
            
// data
            
$arraykeys array_keys($data);
            
$barwidth = (($width $leftmargin) / ($lastxdays count($arraykeys))) - 2;
            for (
$i 0$i $lastxdays$i++) {  // data items
                
for ($j 0$j count($arraykeys); $j++) {  // data groups
                    
$daysago $lastxdays $i;
                    if (
substr($graphmethod0strlen('specific')) == 'specific') {
                        
// specificyear & specificmonth -> reverse order of bottom labels
                        
$xpos $leftmargin + ($daysago * (($width $leftmargin) / $lastxdays)) + ($j $barwidth);
                    } else {
                        
$xpos $leftmargin + ($i * (($width $leftmargin) / $lastxdays)) + ($j $barwidth);
                    }
                    if (isset(
$data[$arraykeys[$j]][$daysago])) {
                        
$ypos $height $bottommargin - (($data[$arraykeys[$j]][$daysago] / $scale) * ($height $bottommargin $topmargin));
                    } else {
                        
$ypos $height $bottommargin;
                    }
                    
ImageFilledRectangle($im$xpos$ypos$xpos $barwidth$height $bottommargin$bar_colors[$j]);     // data
                    
ImageRectangle($im$xpos$ypos$xpos $barwidth$height $bottommargin$border_color);             // border around each bar
                    
$midpoints[$j]['x'][$daysago] = round($xpos + ($barwidth 2));
                    
$midpoints[$j]['y'][$daysago] = round($ypos);
                    
$midpoints['sum']['x'][$daysago] = @$midpoints['sum']['x'][$daysago] + $midpoints[$j]['x'][$daysago];
                }
            }
            foreach (
$midpoints['sum']['x'] as $daysago => $value) {
                
$midpoints['avg']['x'][$daysago] = round($value count($arraykeys));
            }


            
// bottom labels
            
if ($graphmethod == 'pastxdays') {

                if (
$TTFsupport) {

                    
$bottomscale_lastX = ($width 2);
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('d'mktime(000date('n'$lastx_max_date), date('j'$lastx_max_date) - $idate('Y'$lastx_max_date)));
                        
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$labeltext);
                        
$bottomscale_newX $midpoints['avg']['x'][$i] - round($textboundbox[2] / 2);
                        if ((
$bottomscale_lastX $bottomscale_newX) > ($textboundbox[2] * 2)) {
                            
ImageTTFText($im$fontsize0$bottomscale_newX$height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                            
$bottomscale_lastX $bottomscale_newX;
                        }
                    }
                    
// previous & current month names
                    
$text_previousmonthname ' '.date('F Y'$lastx_max_date - (60 60 24 * ($lastxdays 1)));
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$text_previousmonthname);
                    
ImageTTFText($im$fontsize0$leftmargin$height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfile$text_previousmonthname);

                    
$text_currentmonthname date('F Y'$lastx_max_date).' ';
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$text_currentmonthname);
                    
ImageTTFText($im$fontsize0$width $textboundbox[2], $height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfile$text_currentmonthname);

                } else {

                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('d'mktime(000date('n'$lastx_max_date), date('j'$lastx_max_date) - $idate('Y'$lastx_max_date)));
                        
ImageString($im$BuiltInFontSize$midpoints['avg']['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2), $height $bottommargin$labeltext$text_color);
                    }
                    
// previous & current month names
                    
ImageString($im$BuiltInFontSize$leftmargin$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('F Y'$lastx_max_date - (60 60 24 * ($lastxdays 1))), $text_color);
                    
ImageString($im$BuiltInFontSize$width - (ImageFontWidth($BuiltInFontSize) * strlen(date('F Y'$lastx_max_date))) - 5$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('F Y'$lastx_max_date), $text_color);

                }

            } else if (
substr($graphmethod0strlen('specificmonth')) == 'specificmonth') {

                if (
$TTFsupport) {
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$DoW date('w'mktime(1200date('n'$specificmonthdate), $i 1date('Y'$specificmonthdate)));
                        
$labeltext = (string) ($i 1);
                        
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$labeltext);
                        
ImageTTFText($im$fontsize0$midpoints['avg']['x'][$i] - ($textboundbox[2] / 2), $height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                        if ((
$DoW == 0) || ($DoW == 6)) { // weekend
                            // cheap trick for bold text
                            
ImageTTFText($im$fontsize0$midpoints['avg']['x'][$i] - ($textboundbox[2] / 2) + 1$height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                        }
                    }
                    
// month & year
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfiledate('F Y'$specificmonthdate));
                    
ImageTTFText($im$fontsize0, (($width $leftmargin $textboundbox[2]) / 2) + $leftmargin$height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfiledate('F Y'$specificmonthdate));
                } else {
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$DoW date('w'mktime(1200date('n'$specificmonthdate), $i 1date('Y'$specificmonthdate)));
                        
$labeltext = (string) ($i 1);
                        
ImageString($im$BuiltInFontSize$midpoints['avg']['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2), $height $bottommargin $fontsize$i 1$text_color);
                        if ((
$DoW == 0) || ($DoW == 6)) { // weekend
                            // cheap trick for bold text
                            
ImageString($im$BuiltInFontSize$midpoints['avg']['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2) + 1$height $bottommargin $fontsize$i 1$text_color);
                        }
                    }
                    
// month & year
                    
ImageString($im$BuiltInFontSize, (($width $leftmargin - (ImageFontWidth($BuiltInFontSize) * strlen(date('F Y'$specificmonthdate)))) / 2) + $leftmargin$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('F Y'$specificmonthdate), $text_color);
                }

            } else if (
substr($graphmethod0strlen('specificyear')) == 'specificyear') {

                if (
$TTFsupport) {
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('M'mktime(1200$i 1));
                        
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$labeltext);
                        
ImageTTFText($im$fontsize0$midpoints['avg']['x'][$i] - ($textboundbox[2] / 2), $height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                    }
                    
// year
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfiledate('Y'$specificyeardate));
                    
ImageTTFText($im$fontsize0, (($width $leftmargin $textboundbox[2]) / 2) + $leftmargin$height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfiledate('Y'$specificyeardate));
                } else {
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('M'mktime(1200$i 1));
                        
ImageString($im$BuiltInFontSize$midpoints['avg']['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2), $height $bottommargin $fontsize$labeltext$text_color);
                    }
                    
// year
                    
ImageString($im$BuiltInFontSize, (($width $leftmargin - (ImageFontWidth($BuiltInFontSize) * strlen(date('Y'$specificyeardate)))) / 2) + $leftmargin$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('Y'$specificyeardate), $text_color);
                }

            } else if (
$graphmethod == 'pastxmonths') {

                if (
$TTFsupport) {
                    
$textboundbox         ImageTTFBBox($fontsize0$TTFfontfiledate('M'mktime(000date('n'$lastx_max_date) - $i1date('Y'$lastx_max_date))));
                    
$sumwidth             $lastxdays 1.5 $textboundbox[2];
                    
$bottomlabelfrequency 1;
                    while ((
$width $leftmargin) < ($sumwidth $bottomlabelfrequency)) {
                        
$bottomlabelfrequency++;
                    }
                    
$bottomscale_lastX = ($width 2);
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('M'mktime(000date('n'$lastx_max_date) - $i1date('Y'$lastx_max_date)));
                        
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$labeltext);
                        
$bottomscale_newX $midpoints['avg']['x'][$i] - round($textboundbox[2] / 2);
                        if ((
$bottomscale_lastX $bottomscale_newX) > ($textboundbox[2] * 2)) {
                            
ImageTTFText($im$fontsize0$midpoints['avg']['x'][$i] - ($textboundbox[2] / 2), $height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                            
$bottomscale_lastX $bottomscale_newX;
                        }
                    }
                    
// previous & current years
                    
$previous_year_text date('Y'$lastx_max_date - (60 60 24 30 $lastxdays));
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$previous_year_text);
                    
ImageTTFText($im$fontsize0$leftmargin$height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfile$previous_year_text);

                    
$current_year_text date('Y'$lastx_max_date);
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$current_year_text);
                    
ImageTTFText($im$fontsize0$width $textboundbox[2] - 5$height $bottommargin - ($textboundbox[5] * 2.5), $text_color$TTFfontfile$current_year_text);

                } else {

                    
$sumwidth             $lastxdays 1.5 * (ImageFontWidth($BuiltInFontSize) * strlen(date('M'mktime(000date('n'$lastx_max_date) - $i1date('Y'$lastx_max_date)))));
                    
$bottomlabelfrequency 1;
                    while ((
$width $leftmargin) < ($sumwidth $bottomlabelfrequency)) {
                        
$bottomlabelfrequency++;
                    }
                    for (
$i 0$i $lastxdays$i++) { // bottom labels
                        
$labeltext date('M'mktime(000date('n'$lastx_max_date) - $i1date('Y'$lastx_max_date)));
                        
ImageString($im$BuiltInFontSize$midpoints['avg']['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2), $height $bottommargin + ($fontsize 2), $labeltext$text_color);
                    }
                    
// previous & current years
                    
ImageString($im$BuiltInFontSize$leftmargin$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('Y'$lastx_max_date - (60 60 24 30 $lastxdays)), $text_color);
                    
ImageString($im$BuiltInFontSize$width - (ImageFontWidth($BuiltInFontSize) * strlen(date('Y'$lastx_max_date))) - 5$height $bottommargin + ($fontsize 2) + ImageFontHeight($BuiltInFontSize), date('Y'$lastx_max_date), $text_color);
                }
            }

            if (
$PlotAverage) { // average curve
                
$arraykeys array_keys($data);
                for (
$j 0$j count($arraykeys); $j++) {  // data groups
                    
$newX $midpoints[$j]['x'][0];
                    
$newY $midpoints[$j]['y'][0];
                    for (
$i 0$i <= $BezierPrecision$i++) {
                        
$oldX $newX;
                        
$oldY $newY;
                        
$newpoint DeCasteljau($midpoints[$j], $i * ($BezierPrecision));
                        
$newX $newpoint['x'];
                        
$newY $newpoint['y'];
                        
ImageLine($im$oldX$oldY 1$newX$newY 1$line_colors[$j]);
                        
ImageLine($im$oldX$oldY 0$newX$newY 0$line_colors[$j]);
                        
ImageLine($im$oldX$oldY 1$newX$newY 1$line_colors[$j]);
                    }
                }
            }

        } else if (
$graphmethod == '24hourdistribution') {

            
$numberofcolumns 24;

            
// time-of-day labels
            
if ($TTFsupport) {
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile'gh');
                
$time_of_day_label_bottom $height $bottommargin - ($textboundbox[5] * 2.5);
                
ImageTTFText($im$fontsize0$leftmargin$time_of_day_label_bottom$text_color$TTFfontfile'midnight');
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile'noon');
                
ImageTTFText($im$fontsize0$leftmargin + (($width $leftmargin $textboundbox[2]) / 2), $time_of_day_label_bottom$text_color$TTFfontfile'noon');
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile'midnight');
                
ImageTTFText($im$fontsize0$width $textboundbox[2] - 5$time_of_day_label_bottom$text_color$TTFfontfile'midnight');
            } else {
                
ImageString($im$BuiltInFontSize$leftmargin 5$height - (ImageFontHeight($BuiltInFontSize) * 1.5), 'midnight'$text_color);
                
ImageString($im$BuiltInFontSize$leftmargin + (($width - (ImageFontWidth($BuiltInFontSize) * strlen('noon'))) / 2), $height - (ImageFontHeight($BuiltInFontSize) * 1.5), 'noon'$text_color);
                
ImageString($im$BuiltInFontSize$width - (ImageFontWidth($BuiltInFontSize) * strlen('midnight')) - 5$height - (ImageFontHeight($BuiltInFontSize) * 1.5), 'midnight'$text_color);
            }

            
// data
            
$arraykeys array_keys($data);
            
$barwidth = (($width $leftmargin) / ($numberofcolumns count($arraykeys))) - 2;
            for (
$j 0$j count($arraykeys); $j++) {  // data groups
                
for ($i 0$i $numberofcolumns$i++) {  // data items
                    
$xpos $leftmargin + ($i * (($width $leftmargin) / $numberofcolumns)) + ($j $barwidth);
                    
$ypos = ($height $bottommargin $topmargin) - (($data[$arraykeys[$j]][$i] / $scale) * ($height $bottommargin $topmargin)) + $topmargin;
                    
$midpoints[$j]['x'][$i] = round($xpos + ($barwidth 2));
                    
$midpoints[$j]['y'][$i] = $ypos;
                    
ImageFilledRectangle($im$xpos$ypos$xpos $barwidth$height $bottommargin$bar_colors[$j]); // data
                    
ImageRectangle($im$xpos$ypos$xpos $barwidth$height $bottommargin$border_color);           // border around each bar
                
}
                
// make a i+1 point so the graph doesn't end abruptly (and the bezier lines
                
$xpos $leftmargin + (($i 1) * (($width $leftmargin) / $numberofcolumns)) + ($j $barwidth);
                
$midpoints[$j]['x'][] = round($xpos + ($barwidth 2));
                
$midpoints[$j]['y'][] = $midpoints[$j]['y'][0];
            }

            
// bottom labels
            
for ($i 0$i $numberofcolumns$i++) {
                
$labeltext str_pad($i2'0'STR_PAD_LEFT);
                if (
$TTFsupport) {
                    
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$labeltext);
                    
ImageTTFText($im$fontsize0$midpoints[0]['x'][$i] - ($textboundbox[2] / 2), $height $bottommargin - ($textboundbox[5] * 1.25), $text_color$TTFfontfile$labeltext);
                } else {
                    
ImageString($im$BuiltInFontSize$midpoints[0]['x'][$i] - ((ImageFontWidth($BuiltInFontSize) * strlen($labeltext)) / 2), $height $bottommargin + ($fontsize 2), $labeltext$text_color);
                }
            }

            if (
$PlotAverage) { // average curve
                //ImageFilledRectangle($im, 0, 0, $width, $height, $background_color);
                
$arraykeys array_keys($data);
                for (
$j 0$j count($arraykeys); $j++) {  // data groups
                    
$newX $midpoints[$j]['x'][0];
                    
$newY $midpoints[$j]['y'][0];
                    for (
$i 0$i <= $BezierPrecision$i++) {
                        
$oldX $newX;
                        
$oldY $newY;
                        
// $newpoint = DeCasteljau($midpoints[$j], $i);
                        
$newpoint DeCasteljau($midpoints[$j], $i * ($BezierPrecision));
                        
$newX $newpoint['x'];
                        
$newY $newpoint['y'];
                        
ImageLine($im$oldX$oldY 1$newX$newY 1$line_colors[$j]);
                        
ImageLine($im$oldX$oldY 1$newX$newY 1$line_colors[$j]);
                        
ImageLine($im$oldX$oldY 0$newX$newY 0$line_colors[$j]);
                    }
                }
            }
        } else {
            
ImageTTFText($im$fontsize0$width 2$height 2$text_color$TTFfontfile'graphmethod ('.$graphmethod.') not supported!');
        }

        
// legend
        
$xpos $leftmargin + ($fontsize 0.5);
        
$captionwidth 0;
        for (
$j 0$j count($arraykeys); $j++) {
            if (
$TTFsupport) {
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfile$arraykeys[$j]);
                
$captionwidth += ($textboundbox[2] + $fontsize) + ($fontsize 1.5);
            } else {
                
$captionwidth += (ImageFontWidth($BuiltInFontSize) * strlen($arraykeys[$j])) + $fontsize + ($fontsize 1.5);
            }
        }
        
ImageFilledRectangle($im$leftmargin$fontsize 0.3$leftmargin $captionwidth$fontsize 1.7$background_color);
        for (
$j 0$j count($arraykeys); $j++) {
            
ImageFilledRectangle($im$xpos$fontsize 0.5$xpos $fontsize$fontsize 1.5$bar_colors[$j]);
            
$xpos += ($fontsize 1.5);
            if (
$TTFsupport) {
                
$textboundbox ImageTTFBBox($fontsize0$TTFfontfileucfirst($arraykeys[$j]));
                
ImageTTFText($im$fontsize0$xpos$fontsize 1.5$border_color$TTFfontfileucfirst($arraykeys[$j]));
                
$xpos += $textboundbox[2] + $fontsize;
            } else {
                
ImageString($im$BuiltInFontSize$xpos, ($fontsize 1.25) - ImageFontHeight($BuiltInFontSize), ucfirst($arraykeys[$j]), $border_color);
                
$xpos += (ImageFontWidth($BuiltInFontSize) * strlen($arraykeys[$j])) + $fontsize;
            }
        }

        
// border around entire graph
        
ImageRectangle($im00$width 1$height$border_color);

        
DisplayImage($im);
        return 
true;

    } else {

        
ErrorImage('Cannot Initialize new GD image stream');
        return 
false;

    }
}

if (isset(
$_REQUEST['data'])) {

    
// you can pass data to this file (via the GETstring for example) :
    // echo '<img src="lastxgraph.php?data='.serialize($data).'&title='.urlencode('Graph Title').'">';

    
$data $_REQUEST['data'];
    if (!
is_array($data)) {
        
ob_start();
        
$data unserialize($_REQUEST['data']);
        
$errormessage strip_tags(ob_get_contents());
        
ob_end_clean();
        if (!
is_array($data)) {
            
ErrorImage('Passed parameter "data" is neither an array nor a serialized array'.($errormessage "\n\n".$errormessage ''));
            exit;
        }
    }

    
$graphmethod     = (isset($_REQUEST['graphmethod'])     ? $_REQUEST['graphmethod']     : '24hourdistribution');
    
$title           = (isset($_REQUEST['title'])           ? $_REQUEST['title']           : 'Graph Title');
    
$scalelabel      = (isset($_REQUEST['scalelabel'])      ? $_REQUEST['scalelabel']      : 'axis label');
    
$PlotAverage     = (isset($_REQUEST['PlotAverage'])     ? $_REQUEST['PlotAverage']     : true);
    
$BezierPrecision = (isset($_REQUEST['BezierPrecision']) ? $_REQUEST['BezierPrecision'] : 20);
    
$width           = (isset($_REQUEST['width'])           ? $_REQUEST['width']           : 600);
    
$height          = (isset($_REQUEST['height'])          ? $_REQUEST['height']          : 300);
    
$TTFfontfile     = (isset($_REQUEST['TTFfontfile'])     ? $_REQUEST['TTFfontfile']     : 'arial.ttf');
    
$fontsize        = (isset($_REQUEST['fontsize'])        ? $_REQUEST['fontsize']        : 11);
    
$Yaxisdivs       = (isset($_REQUEST['Yaxisdivs'])       ? $_REQUEST['Yaxisdivs']       : 10);
    
$col_background  = (isset($_REQUEST['col_background'])  ? $_REQUEST['col_background']  : 'CCCCCC');
    
$col_border      = (isset($_REQUEST['col_border'])      ? $_REQUEST['col_border']      : '000000');
    
$col_text        = (isset($_REQUEST['col_text'])        ? $_REQUEST['col_text']        : 'FFFFFF');
    
$col_bars        = (isset($_REQUEST['col_bars'])        ? $_REQUEST['col_bars']        : '3399FF;FF9933;FF0000;99FF33;FF33FF');
    
$lastx_max_date  = (isset($_REQUEST['lastx_max_date'])  ? $_REQUEST['lastx_max_date']  : time());

    
LastXgraph($data$graphmethod$title$scalelabel$PlotAverage$BezierPrecision$width$height$TTFfontfile$fontsize$Yaxisdivs$col_background$col_border$col_text$col_bars$lastx_max_date);
    exit;

}

?>