| GNU General Public License for more details.                            |
 +-------------------------------------------------------------------------+ 
 */
-
+// Last Err: 11
 define('CONFIG_DIR', "configs");
 define('CONFIG_FILE', "bacula.conf");
 define('BACULA_TYPE_BYTES_FILES', 1);
 define('BACULA_TYPE_BYTES_ENDTIME_ALLJOBS', 69);
 
 require_once "paths.php";
-require_once "DB.php";                                                                                                                 // Pear DB
+require_once "DB.php";                                                                                                                  // Pear DB
 require_once($smarty_path."Config_File.class.php");
 
-if (!function_exists('array_fill')) {                                                                                  // For PHP < 4.2.0 users 
+if (!function_exists('array_fill')) {                                                                                   // For PHP < 4.2.0 users 
     require_once('array_fill.func.php');
 }
 
 class Bweb extends DB {
 
-       var $StartDate;
-       var $EndDate;
-
-
-
-       function Bweb() {
-               
-               $conf = new Config_File (CONFIG_DIR);
-               
-               
-               $this->dsn[hostspec] = $conf->get(CONFIG_FILE,"DATABASE","host");
-               $this->dsn[username] = $conf->get(CONFIG_FILE,"DATABASE","login");
-               $this->dsn[password] = $conf->get(CONFIG_FILE,"DATABASE","pass");
-               $this->dsn[database] = $conf->get(CONFIG_FILE,"DATABASE","db_name");
-               $this->dsn[phptype] = $conf->get(CONFIG_FILE,"DATABASE","db_type");                     // mysql, (sqlite, pgsql) -->> Yet not checked
-               if (  $conf->get(CONFIG_FILE,"DATABASE","db_port") )
-                       $this->dsn[port] = $conf->get(CONFIG_FILE,"DATABASE","db_port");
-                       
-               $this->link =& $this->connect($this->dsn);
-               
-               if (DB::isError($this->link))
-                   die($this->link->getMessage());
-                               
-               register_shutdown_function(array(&$this,'close'));
-        }
-
-
-               
-       function close() {
-
-               $this->link->disconnect();
-        }      
-
-       
-        
-       function CalculateBytesPeriod($server,$StartDate,$EndPeriod) {                          // Bytes transferred in a period.
-
-               $result =& $this->link->query("select SUM(JobBytes) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
-                       or die("Error query row 68");
-               $return =& $result->fetchRow(); 
-               return $return[0];
-        }//end function
-
-       
-        
-       function CalculateFilesPeriod($server,$StartDate,$EndPeriod) {                          // Number of files transferred in a period.
-
-               $result =& $this->link->query("select SUM(JobFiles) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
-                       or die("Error query row 78");
-               $return =& $result->fetchRow();
-               return $return[0];
-        }//end function 
-
-                
-
-       function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear) {  // Convert date for Smarty. Check if only works with Mysql.
-       
-               $this->StartDate=$StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
-               $this->EndDate=$EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:59";  // last day full
-               
-       }//end function
+        var $StartDate;
+        var $EndDate;
+        var $driver;
+
+
+
+        function Bweb() {
+                
+                $conf = new Config_File (CONFIG_DIR);
+                
+                
+                $this->dsn[hostspec] = $conf->get(CONFIG_FILE,"DATABASE","host");
+                $this->dsn[username] = $conf->get(CONFIG_FILE,"DATABASE","login");
+                $this->dsn[password] = $conf->get(CONFIG_FILE,"DATABASE","pass");
+                $this->dsn[database] = $conf->get(CONFIG_FILE,"DATABASE","db_name");
+                $this->dsn[phptype] = $conf->get(CONFIG_FILE,"DATABASE","db_type");                     // mysql, (sqlite, pgsql) -->> Yet not checked
+                if (  $conf->get(CONFIG_FILE,"DATABASE","db_port") )
+                        $this->dsn[port] = $conf->get(CONFIG_FILE,"DATABASE","db_port");
+                        
+                $this->link =& $this->connect($this->dsn);
+                
+                if (DB::isError($this->link))
+                    die($this->link->getMessage());
+                $this->driver = $this->dsn[phptype];                            
+                register_shutdown_function(array(&$this,'close'));
+         }
+
+
+                
+        function close() {
+
+                $this->link->disconnect();
+         }      
+
+        
+         
+        function CalculateBytesPeriod($server,$StartDate,$EndPeriod) {                          // Bytes transferred in a period.
+
+                $result =& $this->link->query("select SUM(JobBytes) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
+                        or die("classes.inc: Error query: 1");
+                $return =& $result->fetchRow(); 
+                return $return[0];
+         }//end function
+
+        
+         
+        function CalculateFilesPeriod($server,$StartDate,$EndPeriod) {                          // Number of files transferred in a period.
+
+                $result =& $this->link->query("select SUM(JobFiles) from Job WHERE EndTime < '$EndPeriod' and EndTime > '$StartDate' and Name='$server'")
+                        or die("classes.inc: Error query: 2");
+                $return =& $result->fetchRow();
+                return $return[0];
+         }//end function 
+
+                 
+
+        function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear) {  // Convert date for Smarty. Check if only works with Mysql.
+        
+                $this->StartDate=$StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
+                $this->EndDate=$EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:59";  // last day full
+                
+        }//end function
  
 
 
-       function GetDataVolumes() {
-
-               $volume = array();
-               $res = $this->link->query("SELECT Name FROM Pool");
-               while ( $tmp =& $res->fetchRow() ) {
-                       $result = $this->link->query("select Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten,FROM_UNIXTIME(UNIX_TIMESTAMP(Media.LastWritten)+Media.VolRetention ) as expire from Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId where Name='$tmp[0]' order by Media.VolumeName");
-                       while ( $tmp1 = $result->fetchRow() ) {
-                               $pos = array_key_exists($tmp[0],$volume);
-                               if ($pos != FALSE)
-                                       array_push($volume["$tmp[0]"],$tmp1);
-                               else
-                                       $volume += array($tmp[0]=>array($tmp1));
-                       }
-               }
-               
-               $res->free();
-               $result->free();
-               return $volume;
-       }
-       
+        function GetDataVolumes() {
+
+                $volume = array();
+                $res = $this->link->query("SELECT Name FROM Pool");
+                while ( $tmp =& $res->fetchRow() ) {
+                        if ($this->driver == "mysql" )
+                                $result = $this->link->query("select Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten,FROM_UNIXTIME(UNIX_TIMESTAMP(Media.LastWritten)+Media.VolRetention ) as expire from Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId where Name='$tmp[0]' order by Media.VolumeName");
+                        else if ($this->driver == "pgsql")
+                                $result = $this->link->query("select Media.VolumeName, Media.VolBytes,Media.VolStatus,Pool.Name,Media.MediaType,Media.LastWritten,FROM_UNIXTIME(Media.LastWritten::Timestamp without time zone + Media.VolRetention) as expire from Pool LEFT JOIN Media ON Media.PoolId=Pool.PoolId where Name='$tmp[0]' order by Media.VolumeName");                                                          
+                        while ( $tmp1 = $result->fetchRow() ) {
+                                $pos = array_key_exists($tmp[0],$volume);
+                                if ($pos != FALSE)
+                                        array_push($volume["$tmp[0]"],$tmp1);
+                                else
+                                        $volume += array($tmp[0]=>array($tmp1));
+                        }
+                }
+                
+                $res->free();
+                $result->free();
+                return $volume;
+        }
+        
+        function GetDbSize() {
+                if ( $this->driver == "mysql") {
+                        $dbsize = $this->link->query("show table status")
+                                or die ("classes.inc: Error query: 3");
+                        if ( $dbsize->numRows() ) {
+                                while ( $res = $dbsize->fetchRow() )
+                                        $database_size += $res[5];
+                        }
+                        else
+                                return 0;
+                }
+                else if ( $this->driver == "pgsql") {
+                        $dbsize = $this->link->query("select database_size('bacula')")
+                                or die ("classes.inc: Error query: 4");
+                        if ( $dbsize->numRows() ) {
+                                while ( $res = $dbsize->fetchRow() )
+                                        $database_size += $res[0];
+                        }
+                        else
+                                return 0;
+                }       
+        $dbsize->free();
+        return $database_size;  
+        }
+
 }
 
 class BGraph {
 
-       var $type;
-       var $sizex;
-       var $sizey;
-       var $MarginBottom;
-       var $MarginLeft;
-       var $Leg;
-
-
-       
-       function BShowGraph($datos,$title,$xlabel,$ylabel,$leyenda,$tipo="lines") {
-       
-               global $type;
-       
-               require_once ("external_packages/phplot/phplot.php");
-
-               if ( empty($this->sizex) || empty($this->sizey) ) {                                             //Default size
-                       $this->sizex = "600";
-                       $this->sizey = "400";
-               }
-               if ( empty($this->MarginBottom) ) {
-                       $this->MarginBottom = 120;
-               }
-               
-               $legend = $leyenda;
-//             $bgcolor = array(222,206,215);                                                                                  // Background color of graph
-               $bgcolor = array(207,231,231);
-               $fgcolor = array(110,41,57);
-               
-
-               
-               $graph = new PHPlot($this->sizex,$this->sizey,"","");
-
-               if ( !empty($type) )
-                       $graph->setDataType($type);
-
-               $graph->SetDataValues($datos);
-               $graph->SetPlotType($tipo);
-//             $graph->SetUseTTF(1);
-               $graph->SetBackgroundColor($bgcolor);
-
-               $graph->SetLegendPixels(1,20);
-               $graph->SetDataColors(array('SkyBlue','purple','PeachPuff','aquamarine1','#2CB04B','beige','#9F865F','#135568','orchid','navy','red', 'black', 'blue', 'green', 'brown', 'yellow','cyan','orange','#B9F5A7','#AFAFAF'));
-               $graph->SetTitle($title);
-               $graph->SetXLabel($xlabel);
-               $graph->SetYLabel($ylabel);
-               $graph->SetPlotAreaWorld("","","","");
-               
-               if ( count($datos) > 5 )
-                       $graph->SetXDataLabelAngle(90);
-               else
-                       $graph->SetXDataLabelAngle(0);
-//             $graph->SetNumXTicks(10);
-//             $graph->SetXDataLabelPos('none');
-//             $graph->SetXTickLabelPos('plotdown');
-               
-//             $graph->SetXGridLabelType("time");
-//             $graph->SetXTimeFormat("%b ") ;
-
-               if ( $this->Leg == 1 ) {
-                       $this->MarginLeftWithLegend($legend);
-                       $graph->SetMarginsPixels($this->MarginLeft,10,35,$this->MarginBottom);
-                       $graph->SetLegend($legend);                     
-               }
-               else
-                       $graph->SetMarginsPixels(90,35,35,$this->MarginBottom);
-//             $graph->SetDataColors(array($fgcolor),array( "black"));
-               $graph->SetFileFormat( "png");
-//             $graph->DoScaleData(1,1);
-//             $graph->DoMovingAverage(1,1,1);
-               $graph->DrawGraph();
-
-       }//end Crear
+        var $type;
+        var $sizex;
+        var $sizey;
+        var $MarginBottom;
+        var $MarginLeft;
+        var $Leg;
+
+
+        
+        function BShowGraph($datos,$title,$xlabel,$ylabel,$leyenda,$tipo="lines") {
+        
+                global $type;
+        
+                require_once ("external_packages/phplot/phplot.php");
+
+                if ( empty($this->sizex) || empty($this->sizey) ) {                                             //Default size
+                        $this->sizex = "600";
+                        $this->sizey = "400";
+                }
+                if ( empty($this->MarginBottom) ) {
+                        $this->MarginBottom = 120;
+                }
+                
+                $legend = $leyenda;
+//              $bgcolor = array(222,206,215);                                                                                  // Background color of graph
+                $bgcolor = array(207,231,231);
+                $fgcolor = array(110,41,57);
+                
+
+                
+                $graph = new PHPlot($this->sizex,$this->sizey,"","");
+
+                if ( !empty($type) )
+                        $graph->setDataType($type);
+
+                $graph->SetDataValues($datos);
+                $graph->SetPlotType($tipo);
+//              $graph->SetUseTTF(1);
+                $graph->SetBackgroundColor($bgcolor);
+
+                $graph->SetLegendPixels(1,20);
+                $graph->SetDataColors(array('SkyBlue','purple','PeachPuff','aquamarine1','#2CB04B','beige','#9F865F','#135568','orchid','navy','red', 'black', 'blue', 'green', 'brown', 'yellow','cyan','orange','#B9F5A7','#AFAFAF'));
+                $graph->SetTitle($title);
+                $graph->SetXLabel($xlabel);
+                $graph->SetYLabel($ylabel);
+                $graph->SetPlotAreaWorld("","","","");
+                
+                if ( count($datos) > 5 )
+                        $graph->SetXDataLabelAngle(90);
+                else
+                        $graph->SetXDataLabelAngle(0);
+//              $graph->SetNumXTicks(10);
+//              $graph->SetXDataLabelPos('none');
+//              $graph->SetXTickLabelPos('plotdown');
+                
+//              $graph->SetXGridLabelType("time");
+//              $graph->SetXTimeFormat("%b ") ;
+
+                if ( $this->Leg == 1 ) {
+                        $this->MarginLeftWithLegend($legend);
+                        $graph->SetMarginsPixels($this->MarginLeft,10,35,$this->MarginBottom);
+                        $graph->SetLegend($legend);                     
+                }
+                else
+                        $graph->SetMarginsPixels(90,35,35,$this->MarginBottom);
+//              $graph->SetDataColors(array($fgcolor),array( "black"));
+                $graph->SetFileFormat( "png");
+//              $graph->DoScaleData(1,1);
+//              $graph->DoMovingAverage(1,1,1);
+
+//              FIX ME -- to round y axis.
+                $vtick = strlen (round ($graph->max_y));
+                $res = 1;
+                for ($i=1;$i < $vtick; $i++)
+                        $res = $res*10;
+                if (strlen($graph->max_y-$res) != $vtick )
+                        $res = $res/10;
+                $graph->SetVertTickIncrement($res);
+                $graph->DrawGraph();
+
+        }//end Crear
 
 
 //Estupidez que tengo que cambiar. !!!!!!!!!!!
-       function SetDataType($typ) {
-               
-               global $type;
-               $type = $typ;
-       }
-
-       function MarginLeftWithLegend($clients) {
-               
-               $maxlen = 0;
-               
-               while (next($clients)) {
-                       $tmp = strlen (current($clients));
-                       if ( $tmp > $maxlen )
-                               $maxlen = $tmp;
-               }
-               $this->MarginLeft = $maxlen * 11;
-       }       
+        function SetDataType($typ) {
+                
+                global $type;
+                $type = $typ;
+        }
+
+        function MarginLeftWithLegend($clients) {
+                
+                $maxlen = 0;
+                
+                while (next($clients)) {
+                        $tmp = strlen (current($clients));
+                        if ( $tmp > $maxlen )
+                                $maxlen = $tmp;
+                }
+                $this->MarginLeft = $maxlen * 11;
+        }       
 
 }//end class
 
 
 class BCreateGraph extends BGraph {
 
-       var $BD_bacula;
-       var $izquierda;
-       var $derecha;
-       var $StartDate;
-       var $EndDate;
-       var $elapsed;                                                                                                                           // Default elapsed time to show complex graphs
-       
-       
-       
-       function BCreateGraph() {
-       
-               $this->StartDate = "1900-01-01";
-               $this->EndDate = "4000-01-01";
-               $this->elapsed = "86400";                                                                                               // 24 hours in seconds.
-               
-        }              
-        
-        
-        
-       function BCreate($server,$tipo_dato,$title,$tipo="bars",$xlabel="",$ylabel="") {
-       
-               global $DB_bacula;
-               global $izquierda;
-               global $derecha;
-               global $clientes;
-       
-               $this->clientes=array();
-               $DB_bacula = new Bweb();
-               $datos = $this->SQLPrepareData($server,$tipo_dato);
-       
-               if ( empty($datos) ) {                                                                                                  //No data = No stats = Empty graph
-                       header("Content-type: image/png");
-                       $img= @ImageCreate(200,100) or die ("Cannot intialize GD stream");
-                       $bgc= ImageColorAllocate($img, 0, 255,255);
-                       $txc= ImageColorAllocate($img, 0,0,0);
-                       ImageString($img, 5, 4, 4, "None data to process", $txc);
-                       ImagePng($img);
-                       ImageDestroy($img);
-                       return; 
-               }
-       
-               if ( empty ($xlabel) ) {                                                                                                // If no label, table names like leyends
-                       $xlabel=$derecha; $ylabel=$izquierda; 
-               }                                                       
-                       
-               $this->SetDataType("text-data");
-               $this->BShowGraph($datos,$title,$xlabel,$ylabel,$this->clientes,$tipo);
-               
-       }
+        var $BD_bacula;
+        var $izquierda;
+        var $derecha;
+        var $StartDate;
+        var $EndDate;
+        var $elapsed;                                                                                                                           // Default elapsed time to show complex graphs
+        
+        
+        
+        function BCreateGraph() {
+        
+                $this->StartDate = "1900-01-01";
+                $this->EndDate = "4000-01-01";
+                $this->elapsed = "86400";                                                                                               // 24 hours in seconds.
+                
+         }              
+         
+         
+         
+        function BCreate($server,$tipo_dato,$title,$tipo="bars",$xlabel="",$ylabel="") {
+        
+                global $DB_bacula;
+                global $izquierda;
+                global $derecha;
+                global $clientes;
+        
+                $this->clientes=array();
+                $DB_bacula = new Bweb();
+                $datos = $this->SQLPrepareData($server,$tipo_dato);
+        
+                if ( empty($datos) ) {                                                                                                  //No data = No stats = Empty graph
+                        header("Content-type: image/png");
+                        $img= @ImageCreate(200,100) or die ("Cannot intialize GD stream");
+                        $bgc= ImageColorAllocate($img, 0, 255,255);
+                        $txc= ImageColorAllocate($img, 0,0,0);
+                        ImageString($img, 5, 4, 4, "None data to process", $txc);
+                        ImagePng($img);
+                        ImageDestroy($img);
+                        return; 
+                }
+        
+                if ( empty ($xlabel) ) {                                                                                                // If no label, table names like leyends
+                        $xlabel=$derecha; $ylabel=$izquierda; 
+                }                                                       
+                        
+                $this->SetDataType("text-data");
+                $this->BShowGraph($datos,$title,$xlabel,$ylabel,$this->clientes,$tipo);
+                
+        }
 
 
  
-       function SQLPrepareData($servidor,$tipo_dato=0) {                                                       // Prepare bytes data from database.
-
-               global $DB_bacula;
-               global $izquierda;
-               global $derecha;
-       
-               if ( $tipo_dato<30 ) {                                                                                                  // Simple graph. Only 2 data 
-       
-               switch ($tipo_dato)
-                               {
-                               case BACULA_TYPE_BYTES_FILES:
-                                       $izquierda="JobBytes";
-                                       $derecha="JobFiles";
-                                       break;
-                               case BACULA_TYPE_FILES_JOBID:
-                                       $izquierda="JobFiles";
-                                       $derecha="Jobid";
-                                       break;
-                               default:
-                                       $izquierda="JobBytes";
-                                       $derecha="EndTime";
-                                       break;
-                               }
-                       $result = $DB_bacula->link->query("select $derecha,$izquierda from Job where Name='$servidor' and EndTime < '$this->EndDate' and EndTime > '$this->StartDate' order by SchedTime asc")
-                               or die ("Ivalid query row 295");
-               while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
-                       $whole_result[] = array_merge($row["$derecha"],$row[$izquierda]);
-               }
-               $result->free();
-        } else {                                                                                                                               // Complex graph. 3 or more data.
-               
-                       switch ( $tipo_dato )
-                               {
-                               case '30':                                                                                                              // Unused, at this time.
-                                       $result = $DB_bacula->link->query("select JobBytes,JobFiles,Jobid from Job where Name='$servidor' order by EndTime asc")
-                                               or die ("Invalid query row 306");
-                                       while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) )
-                                               $whole_result[] = array_merge($row["Jobid"],$row["JobFiles"],$row["JobBytes"]);
-                                       $result->free();
-                                       break;
-                               case BACULA_TYPE_BYTES_ENDTIME_ALLJOBS:                                                 // Special: Generic graph from all clientes.
-                                       $i = -1;                                                                                                        // Counter of number of jobs of one cliente. SP: Contador del número de jobs totales de un cliente.
-                                       $i2 = 0;                                                                                                        // Counter of number of keys of array. SP: Contador del número de valores del array.
-                                       
-                                       $res = $DB_bacula->link->query("select Name from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed  group by Name order by Name desc")
-                                               or die ("Invalid query row 316");
-                                       $resdata=$DB_bacula->link->query("select date_format(EndTime,\"%Y-%m-%d\") from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed  group by date_format(EndTime,\"%Y-%m-%d\") order by EndTime")
-                                               or die ("Invalid query row 318");
-                                       
-                                       while ( $tmpdata = $res->fetchRow() )
-                                               array_push($this->clientes,$tmpdata[0]);
-                                               
-//                                     echo "<pre>";
-//                                     print_r ($this->clientes);
-//                                     echo "</pre>";
-                                       
-                                       
-                                       $spr = array();                                                                                         // Temporal array
-                                       $spr2 = array();                                                                                        // Temporal array
-                                       $whole_result = array();
-                                       
-                                       while ( $tmpdata = $resdata->fetchRow() ) {
-                                               $count++;
-                                               array_push($spr,$tmpdata[0]);
-                                               $result = $DB_bacula->link->query("select date_format(EndTime,\"%Y-%m-%d\"),SUM(JobBytes),Name,count(Name) as Nname from Job WHERE EndTime like '$tmpdata[0]%' group by Name order by Name desc")
-                                                       or die ("invalid query row 336");
-                                               while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
-                                                       $spr2 = array_merge($spr2,array($row["Name"]=>$row["SUM(JobBytes)"]));
-                                                       $i = $result->numRows();
-                                               }
-
-                                       
-//                                             echo "<pre>";
-//                                             print_r ($spr2);
-//                                             echo "</pre>";
-                                               
-                                               reset ($this->clientes);                                                                
-                                               do { 
-                                                       if ( $spr2[current($this->clientes)] != NULL)
-                                                               array_push($spr,$spr2[current($this->clientes)]);
-                                                       else
-                                                               array_push($spr,0);
-                                               } while ( next($this->clientes) );
-                                               
-                                               if ( $i2 < $i )
-                                                       $i2 = $i;
-                                               
-                                               if ( $tmpdata[0] != $row["EndTime"] )   
-                                                       array_push($whole_result,$spr);
-                                               
-                                               $spr = array();
-                                               $spr2 = array();
-                                       }
-                               
-                                       for ( $i = 0; $i < count($whole_result); $i++ ) {                       // To equal the arrays so that the graph is not unsquared. SP:Igualamos las matrices para que la gráfica no se descuadre
-                                               $tmp = count($whole_result[$i]);
-                                               if ( $i2 < $tmp )                                                                               // Estupidez?. Check this code later...
-                                                       continue;
-                                               $tmp = $i2 - $tmp;
-                                               for ( $a = 0; $a <= $tmp; $a++ )
-                                                       array_push($whole_result[$i],"0");                                      // Fill the array
-                                       }
-                                       $resdata->free();       
-//                                     echo "DEBUG:<br>";
-//                                     echo "<pre>";
-//                                     print_r ($whole_result);
-//                                     echo "</pre>";  
-                                       break;
-                               
-                               default:
-                                       break;
-                       }
-               }
+        function SQLPrepareData($servidor,$tipo_dato=0) {                                                       // Prepare bytes data from database.
+
+                global $DB_bacula;
+                global $izquierda;
+                global $derecha;
+        
+                if ( $tipo_dato<30 ) {                                                                                                  // Simple graph. Only 2 data 
+        
+                switch ($tipo_dato)
+                                {
+                                case BACULA_TYPE_BYTES_FILES:
+                                        $izquierda="jobbytes";
+                                        $derecha="jobfiles";
+                                        break;
+                                case BACULA_TYPE_FILES_JOBID:
+                                        $izquierda="jobfiles";
+                                        $derecha="jobid";
+                                        break;
+                                default:
+                                        $izquierda="jobbytes";
+                                        $derecha="endtime";
+                                        break;
+                                }
+                        $result = $DB_bacula->link->query("select $derecha,$izquierda from Job where Name='$servidor' and EndTime < '$this->EndDate' and EndTime > '$this->StartDate' order by SchedTime asc")
+                                or die ("classes.inc: Error at query: 5");
+                while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
+                        $whole_result[] = $this->array_merge_php4($row["$derecha"],$row[$izquierda]);
+                }
+                $result->free();
+        } else {                                                                                                                                // Complex graph. 3 or more data.
+                
+                        switch ( $tipo_dato )
+                                {
+                                case '30':                                                                                                              // Unused, at this time.
+                                        $result = $DB_bacula->link->query("select JobBytes,JobFiles,Jobid from Job where Name='$servidor' order by EndTime asc")
+                                                or die ("classes.inc: Error at query: 6");
+                                        while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) )
+                                                $whole_result[] = array_merge($row["Jobid"],$row["JobFiles"],$row["JobBytes"]);
+                                        $result->free();
+                                        break;
+                                case BACULA_TYPE_BYTES_ENDTIME_ALLJOBS:                                                 // Special: Generic graph from all clientes.
+                                        $i = -1;                                                                                                        // Counter of number of jobs of one cliente. SP: Contador del número de jobs totales de un cliente.
+                                        $i2 = 0;                                                                                                        // Counter of number of keys of array. SP: Contador del número de valores del array.
+                                        
+                                        $res = $DB_bacula->link->query("select Name from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed  group by Name order by Name desc")
+                                                or die ("classes.inc: Error at query: 7");
+                                        if ($DB_bacula->driver == "mysql")
+                                                $resdata = $DB_bacula->link->query("select date_format(EndTime,\"%Y-%m-%d\") from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed  group by date_format(EndTime, \"%Y-%m-%d\") order by EndTime")
+                                                        or die ("classes.inc: Error at query: 8");
+                                        else if ($DB_bacula->driver == "pgsql")
+                                                $resdata = $DB_bacula->link->query("select date_format(EndTime,'%Y-%m-%d') from Job where UNIX_TIMESTAMP(EndTime) > UNIX_TIMESTAMP(NOW())-$this->elapsed  group by EndTime order by EndTime")                                                   
+                                                        or die ("classes.inc: Error at query: 9");
+                                        
+                                        while ( $tmpdata = $res->fetchRow() )
+                                                array_push($this->clientes,$tmpdata[0]);
+                                                
+//                                      echo "<pre>";
+//                                      print_r ($this->clientes);
+//                                      echo "</pre>";
+                                        
+                                        
+                                        $spr = array();                                                                                         // Temporal array
+                                        $spr2 = array();                                                                                        // Temporal array
+                                        $whole_result = array();
+                                        
+                                        while ( $tmpdata = $resdata->fetchRow() ) {
+                                                $count++;
+                                                array_push($spr,$tmpdata[0]);
+                                                if ($DB_bacula->driver == "mysql")
+                                                        $result = $DB_bacula->link->query("select date_format(EndTime,\"%Y-%m-%d\"),SUM(JobBytes),Name,count(Name) as Nname from Job WHERE EndTime like '$tmpdata[0]%' group by Name order by Name desc")
+                                                                or die ("classes.inc: Error at query: 10");
+                                                else if ($DB_bacula->driver == "pgsql")
+                                                        $result = $DB_bacula->link->query("select date_format(EndTime,'%Y-%m-%d'),SUM(JobBytes),Name,count(Name) as Nname from Job WHERE EndTime like '$tmpdata[0]%' group by EndTime,Name order by Name desc")
+                                                                or die ("classes.inc: Error at query: 11");
+                                                while ( $row = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
+                                                        $spr2 = array_merge($spr2,array($row["Name"]=>$row["SUM(JobBytes)"]));
+                                                        $i = $result->numRows();
+                                                }
+
+                                        
+//                                              echo "<pre>";
+//                                              print_r ($spr2);
+//                                              echo "</pre>";
+                                                
+                                                reset ($this->clientes);                                                                
+                                                do { 
+                                                        if ( $spr2[current($this->clientes)] != NULL)
+                                                                array_push($spr,$spr2[current($this->clientes)]);
+                                                        else
+                                                                array_push($spr,0);
+                                                } while ( next($this->clientes) );
+                                                
+                                                if ( $i2 < $i )
+                                                        $i2 = $i;
+                                                
+                                                if ( $tmpdata[0] != $row["EndTime"] )   
+                                                        array_push($whole_result,$spr);
+                                                
+                                                $spr = array();
+                                                $spr2 = array();
+                                        }
+                                
+                                        for ( $i = 0; $i < count($whole_result); $i++ ) {                       // To equal the arrays so that the graph is not unsquared. SP:Igualamos las matrices para que la gráfica no se descuadre
+                                                $tmp = count($whole_result[$i]);
+                                                if ( $i2 < $tmp )                                                                               // Estupidez?. Check this code later...
+                                                        continue;
+                                                $tmp = $i2 - $tmp;
+                                                for ( $a = 0; $a <= $tmp; $a++ )
+                                                        array_push($whole_result[$i],"0");                                      // Fill the array
+                                        }
+                                        $resdata->free();       
+//                                      echo "DEBUG:<br>";
+//                                      echo "<pre>";
+//                                      print_r ($whole_result);
+//                                      echo "</pre>";  
+                                        break;
+                                
+                                default:
+                                        break;
+                        }
+                }
 //      $result->free();
-         return $whole_result;
-       }//end function
-
-
-
-       //Conversión de la fecha a Mysql-format para Smarty.            THE SAME FUNCTION AT 2 CLASSES. THIS WAY IS BUGGY. TO SOLVE LATER.
-       function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear){
-       
-               $this->StartDate = $StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
-               $this->EndDate = $EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:00";
-               
-       }//end function
-
+          return $whole_result;
+        }//end function
+
+
+
+        //Conversión de la fecha a Mysql-format para Smarty.           THE SAME FUNCTION AT 2 CLASSES. THIS WAY IS BUGGY. TO SOLVE LATER.
+        function PrepareDate($StartDateMonth,$StartDateDay,$StartDateYear,$EndDateMonth,$EndDateDay,$EndDateYear){
+        
+                $this->StartDate = $StartDateYear."-".$StartDateMonth."-".$StartDateDay." 00:00:00";
+                $this->EndDate = $EndDateYear."-".$EndDateMonth."-".$EndDateDay." 23:59:00";
+                
+        }//end function
+
+
+        function array_merge_php4($array1,$array2) {
+            $return=array();
+
+            foreach(func_get_args() as $arg) {
+                if(!is_array($arg)){
+                $arg=array($arg);
+                }
+                    foreach($arg as $key=>$val){
+                            if(!is_int($key)){
+                                $return[$key]=$val;
+                            }else{
+                                $return[]=$val;
+                            }
+                    }
+            }
+        return $return;
+        }
 
 }//end class
 
-?>
\ No newline at end of file
+?>
 
 /* $Id$ */
 
 /*
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Afan Ottenheimer.  Released under
- * the GPL and PHP licenses as stated in the the README file which
- * should have been included with this document.
+ * PHPLOT Version 5.0.rc1
+ * Copyright (C) 1998, 1999, 2000, 2001 Afan Ottenheimer.  Released under
+ * the GPL and PHP licenses as stated in the the README file which should
+ * have been included with this document.
  *
- * Recent (2003-2004) work by Miguel de Benito Delgado <nonick AT 8027 DOT org>
+ * Recent (2003-2004) work by Miguel de Benito Delgado <nonick AT vodafone DOT es>
  *
+ * Requires PHP 4.2.0 or later (CHECK THIS)
  */
 
-
-// PHPLOT Version 5.0.rc1
-// Requires PHP 4.1.0 or later (CHECK THIS)
-
 if (! defined(__FUNCTION__))
     define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.');
 
 define ('MINY', -1);        // Indexes in $data (for DrawXDataLine())
 define ('MAXY', -2);
+define ('TOTY', -3);
 
 error_reporting(E_ALL);
 
-
 class PHPlot {
 
-    /* I have removed internal variable declarations, some isset() checking was required, 
+    /* I have removed internal variable declarations, some isset() checking was required,
      * but now the variables left are those which can be tweaked by the user. This is intended to
-     * be the first step towards moving most of the Set...() methods into a subclass which will be 
+     * be the first step towards moving most of the Set...() methods into a subclass which will be
      * used only when strictly necessary. Many users will be able to put default values here in the
      * class and thus avoid memory overhead and reduce parsing times.
      */
     //////////////// CONFIG PARAMETERS //////////////////////
-       var $reduction = 8;                                     // BW Patch
+
     var $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
     var $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
                                         // (only if is_inline = FALSE also)
 
 //Fonts
     var $use_ttf  = FALSE;                  // Use True Type Fonts?
-    var $ttf_path = '.';                    // Default path to look in for TT Fonts. 
+    var $ttf_path = '.';                    // Default path to look in for TT Fonts.
     var $default_ttfont = 'benjamingothic.ttf';
     var $line_spacing = 4;                  // Pixels between lines.
 
     var $output_file = '';                  // For output to a file instead of stdout
 
 //Data
-    var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-pie
+    var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-single
     var $plot_type= 'linepoints';           // bars, lines, linepoints, area, points, pie, thinbarline, squared
 
     var $label_scale_position = 0.5;        // Shifts data labes in pie charts. 1 = top, 0 = bottom
 //Labels
     // There are two types of labels in PHPlot:
     //    Tick labels: they follow the grid, next to ticks in axis.   (DONE)
-    //                 they are drawn at grid drawing time, by _DrawXTicks() and _DrawYTicks()
+    //                 they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
     //    Data labels: they follow the data points, and can be placed on the axis or the plot (x/y)  (TODO)
-    //                 they are drawn at graph plotting time, by DrawDataLabel(), called by DrawLines(), etc.
-    //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_line
+    //                 they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
+    //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines
 
     // Tick Labels
     var $x_tick_label_pos = 'plotdown';     // plotdown, plotup, both, xaxis, none
     var $x_label_type = '';                 // data, time. Leave blank for no formatting.
     var $y_label_type = '';                 // data, time. Leave blank for no formatting.
     var $x_time_format = '%H:%m:%s';        // See http://www.php.net/manual/html/function.strftime.html
-    var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too... 
+    var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too...
 
     // Skipping labels
     var $x_label_inc = 1;                   // Draw a label every this many (1 = all) (TODO)
     var $y_label_inc = 1;
+    var $_x_label_cnt = 0;                  // internal count FIXME: work in progress
 
     // Legend
     var $legend = '';                       // An array with legend titles
     var $x_tick_cross = 3;                  // ticks cross x axis this many pixels
     var $y_tick_cross = 3;                  // ticks cross y axis this many pixels
 
-    var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none 
-    var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none 
+    var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none
+    var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none
 
     var $num_x_ticks = '';
     var $num_y_ticks = '';
 
-    var $x_tick_increment = '';             // Set num_x_ticks or x_tick_increment, not both.
-    var $y_tick_increment = '';             // Set num_y_ticks or y_tick_increment, not both.
+    var $x_tick_inc = '';                   // Set num_x_ticks or x_tick_inc, not both.
+    var $y_tick_inc = '';                   // Set num_y_ticks or y_tick_inc, not both.
 
     var $skip_top_tick = FALSE;
     var $skip_bottom_tick = FALSE;
     var $draw_y_grid = TRUE;
 
     var $dashed_grid = TRUE;
+    var $grid_at_foreground = FALSE;        // Chooses whether to draw the grid below or above the graph
 
 //Colors and styles       (all colors can be array (R,G,B) or named color)
     var $color_array = 'small';             // 'small', 'large' or array (define your own colors)
     var $line_styles = array('solid', 'solid', 'dashed');   // single value or array
     var $dashed_style = '2-4';              // colored dots-transparent dots
 
-    var $point_size = 5;
-    var $point_shape = 'diamond';           // rect, circle, diamond, triangle, dot, line, halfline, cross
+    var $point_sizes = array(5,5,3);         // single value or array
+    var $point_shapes = array('diamond');   // rect, circle, diamond, triangle, dot, line, halfline, cross
 
     var $error_bar_size = 5;                // right and left size of tee
     var $error_bar_shape = 'tee';           // 'tee' or 'line'
 
     /*!
      * Constructor: Setup img resource, colors and size of the image, and font sizes.
-     * 
+     *
      * \param which_width       int    Image width in pixels.
      * \param which_height      int    Image height in pixels.
      * \param which_output_file string Filename for output.
      * \param which_input_fule  string Path to a file to be used as background.
      */
-    function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) 
+    function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
     {
         /*
          * Please see http://www.php.net/register_shutdown_function
 
         $this->SetRGBArray($this->color_array);
 
-        $this->background_done = FALSE;     // Set to TRUE after background image first drawn
+        $this->background_done = FALSE;     // Set to TRUE after background image is drawn once
 
         if ($which_output_file)
             $this->SetOutputFile($which_output_file);
      * do it ourselves.
      * See notes in the constructor code.
      */
-    function _PHPlot () 
+    function _PHPlot ()
     {
         ImageDestroy($this->img);
         return;
     }
 
 
-/////////////////////////////////////////////    
+/////////////////////////////////////////////
 //////////////                         COLORS
 /////////////////////////////////////////////
 
      * FIXME Isn't this too slow?
      *
      */
-    function SetDefaultStyles() 
+    function SetDefaultStyles()
     {
         /* Some of the Set*() functions use default values when they get no parameters. */
 
         if (! isset($this->session_set)) {
             // If sessions are enabled, this variable will be preserved, so upon future executions, we
-            // will have it set, as well as color names (though not color indices, that's why we 
+            // will have it set, as well as color names (though not color indices, that's why we
             // need to rebuild them)
             $this->session_set = TRUE;
 
             $this->SetLineWidths();
             $this->SetLineStyles();
             $this->SetDefaultDashedStyle($this->dashed_style);
-            $this->SetPointSize($this->point_size);
+            $this->SetPointSizes($this->point_sizes);
         }
 
         $this->SetImageBorderColor($this->i_border);
     /*
      *
      */
-    function SetBackgroundColor($which_color) 
+    function SetBackgroundColor($which_color)
     {
         $this->bg_color= $which_color;
         $this->ndx_bg_color= $this->SetIndexColor($this->bg_color);
     /*
      *
      */
-    function SetPlotBgColor($which_color) 
+    function SetPlotBgColor($which_color)
     {
         $this->plot_bg_color= $which_color;
         $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color);
         return TRUE;
     }
 
-    
+
     /*
      *
      */
         return TRUE;
     }
 
-    
+
     /*
      *
      */
         return TRUE;
     }
 
-    
+
     /*
      *
      */
         return TRUE;
     }
 
-    
+
     /*
      *
      */   
-    function SetTransparentColor($which_color) 
+    function SetTransparentColor($which_color)
     { 
         ImageColorTransparent($this->img, $this->SetIndexColor($which_color));
         return TRUE;
     /*!
      * Sets line widths on a per-line basis.
      */
-    function SetLineWidths($which_lw=NULL) 
+    function SetLineWidths($which_lw=NULL)
     {
         if (is_null($which_lw)) {
             // Do nothing, use default value.
     /*!
      * Sets number of pixels between lines of the same text.
      */
-    function SetLineSpacing($which_spc) 
+    function SetLineSpacing($which_spc)
     {
         $this->line_spacing = $which_spc;
     }
     /*!
      * Sets the default TrueType font and updates all fonts to that.
      */
-    function SetDefaultTTFont($which_font) 
+    function SetDefaultTTFont($which_font)
     {
         if (is_file($which_font) && is_readable($which_font)) {
             $this->default_ttfont = $which_font;
     /*!
      * Sets fonts to their defaults
      */
-    function SetDefaultFonts() 
+    function SetDefaultFonts()
     {
         // TTF:
         if ($this->use_ttf) {
             $this->SetFont('y_label', $this->default_ttfont, 6);
             $this->SetFont('x_title', $this->default_ttfont, 10);
             $this->SetFont('y_title', $this->default_ttfont, 10);
-        } 
+        }
         // Fixed:
         else {
             $this->SetFont('generic', 2);
     /*!
      * Sets Fixed/Truetype font parameters.
      *  \param $which_elem Is the element whose font is to be changed.
-     *         It can be one of 'title', 'legend', 'generic', 
+     *         It can be one of 'title', 'legend', 'generic',
      *         'x_label', 'y_label', x_title' or 'y_title'
-     *  \param $which_font Can be a number (for fixed font sizes) or 
+     *  \param $which_font Can be a number (for fixed font sizes) or
      *         a string with the filename when using TTFonts.
      *  \param $which_size Point size (TTF only)
      * Calculates and updates internal height and width variables.
            break;
         case 'legend':
             $this->legend_font['font'] = $which_font;
-            $this->legend_font['height'] = ImageFontHeight($which_font)-$this->reduction;      // BW Patch
+            $this->legend_font['height'] = ImageFontHeight($which_font);
             $this->legend_font['width'] = ImageFontWidth($which_font);
             break;
         case 'x_label':
 
 
     /*!
-     * Draws a string of text. Horizontal and vertical alignment are relative to 
+     * Draws a string of text. Horizontal and vertical alignment are relative to
      * to the drawing. That is: vertical text (90 deg) gets centered along y-axis 
      * with v_align = 'center', and adjusted to the left of x-axis with h_align = 'right',
-     * 
+     *
      * \note Original multiple lines code submitted by Remi Ricard.
      * \note Original vertical code submitted by Marlin Viss.
      */
                       $which_halign = 'left', $which_valign = 'bottom') 
     {
         // TTF:
-        if ($this->use_ttf) { 
-            $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text); 
+        if ($this->use_ttf) {
+            $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text);
             $rads = deg2rad($which_angle);
 
             if ($which_valign == 'center')
                 $which_xpos -= $size[0] * cos($rads);
 
             ImageTTFText($this->img, $which_font['size'], $which_angle, 
-                         $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text); 
+                         $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text);
         }
         // Fixed fonts:
-        else { 
+        else {
             // Split the text by its lines, and count them
             $which_text = ereg_replace("\r", "", $which_text);
             $str = split("\n", $which_text);
             // (Remember the alignment convention with vertical text)
             if ($which_angle == 90) {
                 // The text goes around $which_xpos.
-                if ($which_halign == 'center') 
+                if ($which_halign == 'center')
                     $which_xpos -= ($nlines * ($which_font['height'] + $spacing))/2;
 
                 // Left alignment requires no modification to $xpos...
                                   $i * ($which_font['height'] + $spacing) + $which_xpos,
                                   $ypos, $str[$i], $which_color);
                 } 
-            } 
+            }
             // Horizontal text:
             else {
                 // The text goes above $which_ypos
                 // valign = 'bottom' requires no modification
 
                 $xpos = $which_xpos;
-                for($i = 0; $i < $nlines; $i++) { 
+                for($i = 0; $i < $nlines; $i++) {
                     // center the text around $which_xpos
                     if ($which_halign == 'center')
                         $xpos = $which_xpos - (strlen($str[$i]) * $which_font['width'])/2;
                 }                 
             }
         } 
-        return TRUE; 
+        return TRUE;
     } // function DrawText()
 
 
 /////////////////////////////////////////////
 
     /*!
-     * Sets output format.
-     * inline condition, cacheable, etc.
+     * Sets output file format.
      */
-    function SetFileFormat($which_file_format) 
+    function SetFileFormat($format)
     {
-        // I know rewriting this was unnecessary, but it didn't work for me, I don't
-        // understand why. 
-        $asked = strtolower($which_file_format);
+        $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__);
+
         switch ($asked) {
         case 'jpg':
             if (imagetypes() & IMG_JPG)
+                $this->file_format = 'jpg';
                 return TRUE;
             break;
         case 'png':
             if (imagetypes() & IMG_PNG)
+                $this->file_format = 'png';
                 return TRUE;
             break;
         case 'gif':
             if (imagetypes() & IMG_GIF)
+                $this->file_format = 'gif';
                 return TRUE;
             break;
         case 'wbmp':
             if (imagetypes() & IMG_WBMP)
+                $this->file_format = 'wbmp';
                 return TRUE;
             break;
         default:
-            $this->PrintError("SetFileFormat(): Unrecognized option '$which_file_format'");
+            $this->PrintError("SetFileFormat():File format '$format' not supported");
             return FALSE;
         }
-        $this->PrintError("SetFileFormat():File format '$which_file_format' not supported");
-        return FALSE;
-    }    
+    }
 
 
     /*!
-     * Selects an input file to be used as background for the whole graph.
+     * Selects an input file to be used as graph background and scales or tiles this image
+     * to fit the sizes.
+     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
+     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
      */
-    function SetInputFile($which_input_file) 
-    { 
-        $size = GetImageSize($which_input_file);
-        $input_type = $size[2]; 
-
-        switch($input_type) {
-        case 1:
-            $im = @ImageCreateFromGIF ($which_input_file);
-            if (!$im) { // See if it failed 
-                $this->PrintError("Unable to open $which_input_file as a GIF");
-                return FALSE;
-            }
-        break;
-        case 3:
-            $im = @ImageCreateFromPNG ($which_input_file); 
-            if (!$im) { // See if it failed 
-                $this->PrintError("Unable to open $which_input_file as a PNG");
-                return FALSE;
-            }
-        break;
-        case 2:
-            $im = @ImageCreateFromJPEG ($which_input_file); 
-            if (!$im) { // See if it failed 
-                $this->PrintError("Unable to open $which_input_file as a JPG");
-                return FALSE;
-            }
-        break;
-        default:
-            $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!');
-            return FALSE;
-        break;
-        }
-
-        // Set Width and Height of Image
-        $this->image_width = $size[0];
-        $this->image_height = $size[1];
-
-        // Deallocate any resources previously allocated
-        if ($this->img)
-            imagedestroy($this->img);
-
-        $this->img = $im;
-
-        return TRUE;
+    function SetBgImage($input_file, $mode='centeredtile')
+    {
+        $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
+        $this->bgimg  = $input_file;
+    }
 
+    /*!
+     * Selects an input file to be used as plot area background and scales or tiles this image
+     * to fit the sizes.
+     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
+     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
+     */
+    function SetPlotAreaBgImage($input_file, $mode='tile')
+    {
+        $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
+        $this->plotbgimg  = $input_file;
     }
 
-    function SetOutputFile($which_output_file) 
-    { 
+
+    /*!
+     * Sets the name of the file to be used as output file.
+     */
+    function SetOutputFile($which_output_file)
+    {
         $this->output_file = $which_output_file;
         return TRUE;
     }
 
     /*!
-     * Sets the output image as 'inline', ie. no Content-Type headers are sent
-     * to the browser. Very useful if you want to embed the images.
+     * Sets the output image as 'inline', that is: no Content-Type headers are sent
+     * to the browser. Needed if you want to embed the images.
      */
-    function SetIsInline($which_ii) 
+    function SetIsInline($which_ii)
     {
-        $this->is_inline = $which_ii;
+        $this->is_inline = (bool)$which_ii;
         return TRUE;
     }
 
      * Performs the actual outputting of the generated graph, and
      * destroys the image resource.
      */
-    function PrintImage() 
+    function PrintImage()
     {
         // Browser cache stuff submitted by Thiemo Nagel
         if ( (! $this->browser_cache) && (! $this->is_inline)) {
     /*!
      * Sets position for X labels following data points.
      */
-    function SetXDataLabelPos($which_xdlp) 
+    function SetXDataLabelPos($which_xdlp)
     {
         $this->x_data_label_pos = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none',
                                                       __FUNCTION__);
         if ($which_xdlp != 'none')
-            $this->x_tick_label_pos == 'none';
+            $this->x_tick_label_pos = 'none';
 
         return TRUE;
     }
         $this->y_data_label_pos = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, none',
                                                       __FUNCTION__);
         if ($which_ydlp != 'none')
-            $this->y_tick_label_pos == 'none';
+            $this->y_tick_label_pos = 'none';
 
         return TRUE;
     }
         $this->x_tick_label_pos = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none',
                                                       __FUNCTION__);
         if ($which_xtlp != 'none')
-            $this->x_data_label_pos == 'none';
+            $this->x_data_label_pos = 'none';
 
         return TRUE;
     }
         $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none',
                                                       __FUNCTION__);
         if ($which_ytlp != 'none')
-            $this->y_data_label_pos == 'none';
+            $this->y_data_label_pos = 'none';
 
         return TRUE;
     }
 /////////////////////////////////////////////
 
     /*!
-     * ON THE WORKS:        XXX XXX XXX XXX
-     *
-     *****************                       __FUNCTION__ needs PHP 4.3.0
-     *
      * Checks the valididy of an option.
      *  \param which_opt  String to check.
      *  \param which_acc  String of accepted choices.
         if ($asked == '')
             return '';
 
+        $asked = strtolower($asked);
         if (@ eregi($asked, $which_acc)) {
             return $asked;
         } else {
             $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
             return NULL;
-        } 
+        }
     }
 
 
     /*!
      *  \note Submitted by Thiemo Nagel
      */
-    function SetBrowserCache($which_browser_cache) 
+    function SetBrowserCache($which_browser_cache)
     {
         $this->browser_cache = $which_browser_cache;
         return TRUE;
     /*!
      * Whether to show the final image or not
      */
-    function SetPrintImage($which_pi) 
+    function SetPrintImage($which_pi)
     {
         $this->print_image = $which_pi;
         return TRUE;
      */
     function SetLegendWorld($which_x, $which_y, $which_type=NULL) 
     { 
-        if (! $this->scale_is_set) 
+        if (! isset($this->scale_is_set))
             $this->CalcTranslation();
 
         $this->legend_x_pos = $this->xtr($which_x);
     /*!
      * Accepted values are: left, sides, none, full
      */
-    function SetPlotBorderType($pbt) 
+    function SetPlotBorderType($pbt)
     {
         $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
     }
     /*!
      * \param dpab bool
      */
-    function SetDrawPlotAreaBackground($dpab) 
+    function SetDrawPlotAreaBackground($dpab)
     {
         $this->draw_plot_area_background = (bool)$dpab;
     }
         $this->draw_y_data_label_lines = $dydl;
         return TRUE;
     }
+    
     /*!
      * Sets the graph's title.
+     * TODO: add parameter to choose title placement: left, right, centered=
      */
     function SetTitle($which_title) 
     {
     function SetPlotType($which_pt) 
     {
         $this->plot_type = $this->CheckOption($which_pt, 
-                                  'bars, lines, linepoints, area, points, pie, thinbarline, squared', 
-                                  __FUNCTION__);
+                           'bars, stackedbars, lines, linepoints, area, points, pie, thinbarline, squared', 
+                            __FUNCTION__);
     }
 
     /*!
      * Sets the position of Y axis.
-     * \param pos int Position in world coordinates. 
+     * \param pos int Position in world coordinates.
      */
-    function SetYAxisPosition($pos) 
+    function SetYAxisPosition($pos)
     {
         $this->y_axis_position = (int)$pos;
         if (isset($this->scale_is_set)) {
      * Sets the position of X axis.
      * \param pos int Position in world coordinates. 
      */
-    function SetXAxisPosition($pos) 
+    function SetXAxisPosition($pos)
     {
         $this->x_axis_position = (int)$pos;
         if (isset($this->scale_is_set)) {
     }
 
 
-    function SetXScaleType($which_xst) 
-    { 
+    function SetXScaleType($which_xst)
+    {
         $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
         return TRUE;
     }
 
-    function SetYScaleType($which_yst) 
-    { 
+    function SetYScaleType($which_yst)
+    {
         $this->yscale_type = $this->CheckOption($which_yst, 'linear, log',  __FUNCTION__);
         return TRUE;
     }
 
-    function SetPrecisionX($which_prec) 
+    function SetPrecisionX($which_prec)
     {
         $this->x_precision = $which_prec;
+        $this->SetXLabelType('data');
         return TRUE;
     }
-    function SetPrecisionY($which_prec) 
+
+    function SetPrecisionY($which_prec)
     {
         $this->y_precision = $which_prec;
+        $this->SetYLabelType('data');
         return TRUE;
     }
 
-    function SetErrorBarLineWidth($which_seblw) 
+    function SetErrorBarLineWidth($which_seblw)
     {
         $this->error_bar_line_width = $which_seblw;
         return TRUE;
     }
 
-    function SetLabelScalePosition($which_blp) 
+    function SetLabelScalePosition($which_blp)
     {
         //0 to 1
         $this->label_scale_position = $which_blp;
         return TRUE;
     }
 
-    function SetErrorBarSize($which_ebs) 
+    function SetErrorBarSize($which_ebs)
     {
         //in pixels
         $this->error_bar_size = $which_ebs;
     /*!
      * Can be one of: 'tee', 'line'
      */
-    function SetErrorBarShape($which_ebs) 
+    function SetErrorBarShape($which_ebs)
     {
         $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
     }
 
     /*!
-     * Can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
+     * Sets point shape for each data set via an array.
+     * Shape can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
      * 'diamond', 'triangle', 'trianglemid'
      */
-    function SetPointShape($which_pt) 
+    function SetPointShapes($which_pt)
     {
-        $this->point_shape = $this->CheckOption($which_pt, 
-                              'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid',
-                              __FUNCTION__);
+        if (is_null($which_pt)) {
+            // Do nothing, use default value.
+        } else if (is_array($which_pt)) {
+            // Did we get an array with point shapes?
+            $this->point_shapes = $which_pt;
+        } else {
+            // Single value into array
+            $this->point_shapes = array($which_pt);
+        }
+
+        foreach ($this->point_shapes as $shape)
+        {
+            // TODO, better check, per element rectification
+            $this->CheckOption($shape,
+               'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid',
+                __FUNCTION__);
+        }
+
+        // Make both point_shapes and point_sizes same size.
+        $ps = count($this->point_sizes);
+        $pt = count($this->point_shapes);
+
+        if ($ps < $pt) {
+            array_pad_array($this->point_sizes, $pt);
+        } else if ($pt > $ps) {
+            array_pad_array($this->point_shapes, $ps);
+        }
+        return TRUE;
     }
 
     /*!
      * Sets the point size for point plots.
      * \param ps int Size in pixels.
+     * \note Test this more extensively
      */
-    function SetPointSize($ps) 
+    function SetPointSizes($which_ps)
     {
-        $this->point_size = (int)$ps;
+        if (is_null($which_ps)) {
+            // Do nothing, use default value.
+        } else if (is_array($which_ps)) {
+            // Did we get an array with point sizes?
+            $this->point_sizes = $which_ps;
+        } else {
+            // Single value into array
+            $this->point_sizes = array($which_ps);
+        }
+
+        // Make both point_shapes and point_sizes same size.
+        $ps = count($this->point_sizes);
+        $pt = count($this->point_shapes);
+
+        if ($ps < $pt) {
+            array_pad_array($this->point_sizes, $pt);
+        } else if ($pt > $ps) {
+            array_pad_array($this->point_shapes, $ps);
+        }
 
-        if ($this->point_shape == 'diamond' or $this->point_shape == 'triangle') {
-            if ($this->point_size % 2 != 0) {
-                $this->point_size++;
+        // Fix odd point sizes for point shapes which need it
+        for ($i = 0; $i < $pt; $i++) {
+            if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle') {
+                if ($this->point_sizes[$i] % 2 != 0) {
+                    $this->point_sizes[$i]++;
+                }
             }
         }
         return TRUE;
 
     /*!
      *  text-data: ('label', y1, y2, y3, ...)
-     *  text-data-pie: ('label', y1), for pie charts. See DrawPieChart()
+     *  text-data-single: ('label', data), for some pie charts.
      *  data-data: ('label', x, y1, y2, y3, ...)
      *  data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...)
      */
-    function SetDataType($which_dt) 
+    function SetDataType($which_dt)
     {
-        //The next three lines are for past compatibility.
+        //The next four lines are for past compatibility.
         if ($which_dt == 'text-linear') { $which_dt = 'text-data'; };
         if ($which_dt == 'linear-linear') { $which_dt = 'data-data'; };
         if ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; };
+        if ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; }
+
 
-        $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-pie, data-data, data-data-error',
-                                              __FUNCTION__);
+        $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '.
+                                                         'data-data, data-data-error', __FUNCTION__);
         return TRUE;
     }
 
      * use for (or while) loops, which sometimes are faster. Performance improvements
      * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions.
      */
-    function SetDataValues(&$which_dv) 
+    function SetDataValues(&$which_dv)
     {
+        unset ($this->data_limits_done);        // Reset this for every new data_set
         $this->num_data_rows = count($which_dv);
         $this->total_records = 0;               // Perform some useful calculations.
         $this->records_per_group = 1;           
         array_pad_array($this->line_styles, $this->records_per_group);
 
         array_pad_array($this->data_colors, $this->records_per_group);
-        array_pad_array($this->data_border_colors, $this->records_per_group); 
+        array_pad_array($this->data_border_colors, $this->records_per_group);
         array_pad_array($this->error_bar_colors, $this->records_per_group);
 
         $this->SetDataColors();
      *   Text-Data is different than data-data graphs. For them what
      *   we have, instead of X values, is # of records equally spaced on data.
      *   text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...)
-     *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...) 
+     *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...)
      */
-    function FindDataLimits() 
+    function FindDataLimits()
     {
         // Set some default min and max values before running through the data
         switch ($this->data_type) {
             $maxy = $miny;
             break;
         }
-        
-        $mine = 0;  // Maximum value for the -error bar (assume error bars always > 0) 
-        $maxe = 0;  // Maximum value for the +error bar (assume error bars always > 0) 
+
+        $mine = 0;  // Maximum value for the -error bar (assume error bars always > 0)
+        $maxe = 0;  // Maximum value for the +error bar (assume error bars always > 0)
         $maxt = 0;  // Maximum number of characters in text labels
-        
+
         $minminy = $miny;
         $maxmaxy = $maxy;
+
+        if ($this->plot_type == 'stackedbars') { $maxmaxy = $minminy = 0; }
+
         // Process each row of data
         for ($i=0; $i < $this->num_data_rows; $i++) {
             $j=0;
             $val = @ strlen($this->data[$i][$j++]);
             $maxt = ($val > $maxt) ? $val : $maxt;
 
+
+            if ($this->plot_type == 'stackedbars') { $maxy = $miny = 0; }
+
             switch ($this->data_type) {
             case 'text-data':           // Data is passed in as (title, y1, y2, y3, ...)
-            case 'text-data-pie':       // This one is for some pie charts, see DrawPieChart()
+            case 'text-data-single':    // This one is for some pie charts
                 // $numrecs = @ count($this->data[$i]);
                 $miny = $maxy = (double)$this->data[$i][$j];
                 for (; $j < $this->num_recs[$i]; $j++) {
                     $val = (double)$this->data[$i][$j];
-                    $maxy = ($val > $maxy) ? $val : $maxy;
-                    $miny = ($val < $miny) ? $val : $miny;
+                    if ($this->plot_type == 'stackedbars') {
+                        $maxy += abs($val);      // only positive values for the moment
+                    } else {
+                        $maxy = ($val > $maxy) ? $val : $maxy;
+                        $miny = ($val < $miny) ? $val : $miny;
+                    }
                 }
                 break;
-            case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...) 
+            case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...)
                 // X value:
                 $val = (double)$this->data[$i][$j++];
                 $maxx = ($val > $maxx) ? $val : $maxx;
                 $minx = ($val < $minx) ? $val : $minx;
-                
+
                 $miny = $maxy = (double)$this->data[$i][$j];
                 // $numrecs = @ count($this->data[$i]);
                 for (; $j < $this->num_recs[$i]; $j++) {
             }
             $this->data[$i][MINY] = $miny;      // This row's min Y, for DrawXDataLine()
             $this->data[$i][MAXY] = $maxy;      // This row's max Y, for DrawXDataLine()
+
             $minminy = ($miny < $minminy) ? $miny : $minminy;   // global min
             $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy;   // global max
         }
         $this->max_t = $maxt;
 
         $this->data_limits_done = TRUE;
-        
+
         return TRUE;
     }
 
      * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos
      *        maybe it shouldn't be so...
      *
+     * FIXME: y_data_label_pos is not yet used...
+     *
      * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate
-     *       max_x_labels and max_y_labels, to be used by drawing functions t avoid overlapping.
+     *       max_x_labels and max_y_labels, to be used by drawing functions to avoid overlapping.
      */
-    function CalcMargins() 
+    function CalcMargins()
     {
         // Temporary variables for label size calculation
         $xlab = $this->FormatLabel('x', $this->max_x);
         $ylab = $this->FormatLabel('y', $this->max_y);
 
+        // dirty fix:
+        // max_t is the maximum data label length (from column 0 of each data row).
+        if ($this->max_t > strlen ($xlab))
+            $xlab = sprintf ("%{$this->max_t}s","_");
+
         //////// Calculate maximum X/Y axis label height and width:
 
         // TTFonts:
             $this->x_tick_label_height = $size[1];
 
             // Maximum Y axis label width
-            $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle, 
+            $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle,
                                         $this->y_label_font['font'], $ylab);
             $this->y_tick_label_width = $size[0];
         }
         // Fixed fonts:
-        else { 
+        else {
             // Maximum X axis label height
             if ($this->x_label_angle == 90)
                 $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width'];
-            else 
+            else
                 $this->x_tick_label_height = $this->x_label_font['height'];
 
             // Maximum Y axis label width
             $this->y_top_margin += $this->x_tick_label_height;
 
         // Lower title, ticks and tick labels, and data labels:
-        $this->y_bot_margin = $this->safe_margin * 2; 
+        $this->y_bot_margin = $this->safe_margin * 2;
 
         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
             $this->y_bot_margin += $this->x_title_height;
     /*!
      * Set the margins in pixels (left, right, top, bottom)
      */
-    function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm) 
+    function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
     { 
 
         $this->x_left_margin = $which_lm;
 
     /*!
      * Sets the limits for the plot area. If no arguments are supplied, uses
-     * values calculated from _CalcMargins();
+     * values calculated from CalcMargins();
      * Like in GD, (0,0) is upper left
      *
      * This resets the scale if SetPlotAreaWorld() was already called
      */
     function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) 
     {
-        if ((! $xmin)  && (! $xmax) ) {
-            // For automatic setting of data we need data limits
-            if (! isset($this->data_limits_done)) {
-                $this->FindDataLimits() ;
-            }
-            if ($this->data_type == 'text-data') {
-                $xmax = $this->max_x + 1 ;  // valid for BAR CHART TYPE GRAPHS ONLY
-                $xmin = 0 ;                 // valid for BAR CHART TYPE GRAPHS ONLY
-            } else {
-                $xmax = $this->max_x;
+        if (! isset($this->data_limits_done)) { // For automatic setting of data we need data limits
+            $this->FindDataLimits() ;
+        }
+ 
+        if ($xmin === NULL || $xmin === '') {
+            if ($this->data_type == 'text-data')  // Valid for data without X values only.
+                $xmin = 0;
+            else
                 $xmin = $this->min_x;
-            }
-
-            $ymax = ceil($this->max_y * 1.1);
-            if ($this->min_y < 0) {
-                $ymin = floor($this->min_y * 1.1);
-            } else {
-                $ymin = 0;
-            }
+        }
+        if ($xmax === NULL || $xmax === '') {
+            if ($this->data_type == 'text-data')  // Valid for data without X values only.
+                $xmax = $this->max_x + 1;
+            else
+                $xmax = $this->max_x;
         }
 
-        $this->plot_min_x = $xmin;
-        $this->plot_max_x = $xmax;
-
-        if ($ymin == $ymax) {
-            $ymax += 1;
+        // Leave room above and below the highest and lowest data points.
+        
+        if ($ymin === NULL || $ymin === '') {
+            if ($this->min_y < 0)
+                $ymin = ceil($this->min_y * 1.1);
+            else
+                $ymin = floor($this->min_y * 0.9);
+        }    
+        if ($ymax === NULL || $ymax === '') {
+            if ($this->max_y < 0)
+                $ymax = floor($this->max_y * 0.9);
+            else
+                $ymax = ceil($this->max_y * 1.1);
         }
-        if ($this->yscale_type == 'log') { 
-            //extra error checking
+        
+        // Error checking
+        
+        if ($ymin == $ymax)     // Minimum height
+            $ymax += 1;
+
+        if ($this->yscale_type == 'log') {
             if ($ymin <= 0) { 
                 $ymin = 1;
             }
                 return FALSE;
             }
         }
-
-        $this->plot_min_y = $ymin;
-        $this->plot_max_y = $ymax;
-
+        
         if ($ymax <= $ymin) {
             $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min');
             return FALSE;
         }
+       
+      
+        // Reset (if it was already set) the scale with the new maxs and mins
+      
+        $this->plot_min_x = $xmin;
+        $this->plot_max_x = $xmax;
+        $this->plot_min_y = $ymin;
+        $this->plot_max_y = $ymax;
 
-        // Reset the scale with the new maxs and mins
         if (isset($this->plot_area_width)) {
             $this->CalcTranslation();
         }
 
 
     /*!
-     * For plots that have equally spaced x variables and multiple bars per x-point.
+     * For bar plots, which have equally spaced x variables.
      */
-    function SetEqualXCoord() 
+    function CalcBarWidths() 
     {
-        $space = ($this->plot_area[2] - $this->plot_area[0]) / 
-                 ($this->num_data_rows * 2) * $this->group_frac_width;
-        $group_width = $space * 2;
-        $bar_width = $group_width / $this->records_per_group;
-        //I think that eventually this space variable will be replaced by just graphing x.
-        $this->data_group_space = $space;
-        $this->record_bar_width = $bar_width;
+        $group_width = ($this->plot_area[2] - $this->plot_area[0]) /
+                      $this->num_data_rows * $this->group_frac_width;
+        if ($this->plot_type == 'bars') {
+            $this->record_bar_width = $group_width / $this->records_per_group;
+        } else if ($this->plot_type == 'stackedbars') {
+            $this->record_bar_width = $group_width;
+        }            
+        $this->data_group_space = $group_width / 2;
         return TRUE;
     }
 
     /*!
      * Calculates scaling stuff...
      */
-    function CalcTranslation() 
+    function CalcTranslation()
     {
-        if ($this->xscale_type == 'log') { 
-            $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
-        } else { 
-            $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
-        }
-        if ($this->yscale_type == 'log') { 
-            $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
-        } else { 
-            $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
+        if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0
+            $this->xscale = 0;
+        } else {
+            if ($this->xscale_type == 'log') {
+                $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
+            } else {
+                $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
+            }
         }
 
+        if ($this->plot_max_y - $this->plot_min_y == 0) { // Check for div by 0
+            $this->yscale = 0;
+        } else {
+            if ($this->yscale_type == 'log') {
+                $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
+            } else {
+                $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
+            }
+        }
         // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
-        if ($this->xscale_type == 'log') { 
+        if ($this->xscale_type == 'log') {
             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) );
-        } else { 
+        } else {
             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x);
         }
-        if ($this->yscale_type == 'log') { 
+        if ($this->yscale_type == 'log') {
             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
         } else { 
             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
         // There should be a better place for this.
 
         // User provided y axis position?
-        if ($this->y_axis_position != '') { 
+        if ($this->y_axis_position != '') {
             // Make sure we draw our axis inside the plot
-            $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x) 
+            $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x)
                                      ? $this->plot_min_x : $this->y_axis_position;
-            $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x) 
+            $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x)
                                      ? $this->plot_max_x : $this->y_axis_position;
             $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
-        } else { 
+        } else {
             // Default to left axis
             $this->y_axis_x_pixels = $this->xtr($this->plot_min_x);
         }
         // User provided x axis position?
-        if ($this->x_axis_position != '') { 
+        if ($this->x_axis_position != '') {
             // Make sure we draw our axis inside the plot
-            $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y) 
+            $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y)
                                      ? $this->plot_min_y : $this->x_axis_position;
-            $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y) 
+            $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y)
                                      ? $this->plot_max_y : $this->x_axis_position;
             $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
         } else { 
             if ($this->yscale_type == 'log')
                 $this->x_axis_y_pixels = $this->ytr(1);
-            else 
+            else
                 // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld())
                 $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) 
                                          ? $this->ytr(0) : $this->ytr($this->plot_min_y);
      */
     function ytr($y_world) 
     {
-        if ($this->yscale_type == 'log') { 
+        if ($this->yscale_type == 'log') {
             //minus because GD defines y = 0 at top. doh!
-            $y_pixels =  $this->plot_origin_y - log10($y_world) * $this->yscale ;  
+            $y_pixels =  $this->plot_origin_y - log10($y_world) * $this->yscale ;
         } else { 
             $y_pixels =  $this->plot_origin_y - $y_world * $this->yscale ;  
         }
      *
      * \note Time formatting suggested by Marlin Viss
      */
-    function FormatLabel($which_pos, $which_lab) 
-    { 
+    function FormatLabel($which_pos, $which_lab)
+    {
         switch ($which_pos) {
         case 'x':
         case 'plotx':
             switch ($this->x_label_type) {
             case 'title':
-                $lab = $this->data[$which_lab][0];
+                $lab = @ $this->data[$which_lab][0];
                 break;
             case 'data':
-                $lab = number_format($which_lab, $this->x_precision, '.', ', ').$this->data_units_text;
+                $lab = number_format($which_lab, $this->x_precision, '.', ',').$this->data_units_text;
                 break;
             case 'time':
                 $lab = strftime($this->x_time_format, $which_lab);
                 // Unchanged from whatever format it is passed in
                 $lab = $which_lab;
             break;
-            }    
+            }
             break;
         case 'y':
         case 'ploty':
             switch ($this->y_label_type) {
             case 'data':
-                $lab = number_format($which_lab, $this->y_precision, '.', ', ').$this->data_units_text;
+                $lab = number_format($which_lab, $this->y_precision, '.', ',').$this->data_units_text;
                 break;
             case 'time':
                 $lab = strftime($this->y_time_format, $which_lab);
         default:
             $this->PrintError("FormatLabel(): Unknown label type $which_type");
             return NULL;
-        } 
+        }
 
         return $lab;
     } //function FormatLabel
     /*!
      * Use either this or SetNumXTicks() to set where to place x tick marks
      */
-    function SetXTickIncrement($which_ti=NULL) 
+    function SetXTickIncrement($which_ti=NULL)
     {
         if ($which_ti) {
-            $this->x_tick_increment = $which_ti;  //world coordinates
+            $this->x_tick_inc = $which_ti;  //world coordinates
         } else {
             if (! isset($this->data_limits_done)) {
                 $this->FindDataLimits();  //Get maxima and minima for scaling
             }
-            //$this->x_tick_increment = ( ceil($this->max_x * 1.2) - floor($this->min_x * 1.2) )/10;
-            $this->x_tick_increment =  ($this->plot_max_x  - $this->plot_min_x  )/10;
+            $this->x_tick_inc =  ($this->plot_max_x  - $this->plot_min_x  )/10;
         }
-        $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_increment, not both
+        $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
         return TRUE;
     }
 
     /*!
      * Use either this or SetNumYTicks() to set where to place y tick marks
      */
-    function SetYTickIncrement($which_ti=NULL) 
+    function SetYTickIncrement($which_ti=NULL)
     {
         if ($which_ti) {
-            $this->y_tick_increment = $which_ti;  //world coordinates
+            $this->y_tick_inc = $which_ti;  //world coordinates
         } else {
             if (! isset($this->data_limits_done)) {
                 $this->FindDataLimits();  //Get maxima and minima for scaling
             if (! isset($this->plot_max_y))
                 $this->SetPlotAreaWorld();
 
-            //$this->y_tick_increment = ( ceil($this->max_y * 1.2) - floor($this->min_y * 1.2) )/10;
-            $this->y_tick_increment =  ($this->plot_max_y  - $this->plot_min_y  )/10;
+            $this->y_tick_inc =  ($this->plot_max_y  - $this->plot_min_y  )/10;
         }
-        $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_increment, not both
+        $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
         return TRUE;
     }
 
     function SetNumXTicks($which_nt) 
     {
         $this->num_x_ticks = $which_nt;
-        $this->x_tick_increment = '';  //either use num_x_ticks or x_tick_increment, not both
+        $this->x_tick_inc = '';  //either use num_x_ticks or x_tick_inc, not both
         return TRUE;
     }
 
     function SetNumYTicks($which_nt) 
     {
         $this->num_y_ticks = $which_nt;
-        $this->y_tick_increment = '';  //either use num_y_ticks or y_tick_increment, not both
+        $this->y_tick_inc = '';  //either use num_y_ticks or y_tick_inc, not both
         return TRUE;
     }
 
     /*!
      *
      */
-    function SetXTickPos($which_tp) 
+    function SetXTickPos($which_tp)
     { 
-        $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__); 
+        $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__);
         return TRUE;
     }
 
         return TRUE;
     }
 
-    function SetXTickLength($which_xln) 
+    /*!
+     * \param skip bool
+     */ 
+    function SetSkipLeftTick($skip)
+    {
+        $this->skip_left_tick = (bool)$skip;
+        return TRUE;
+    }
+
+    /*!
+     * \param skip bool
+     */
+    function SetSkipRightTick($skip)
+    {
+        $this->skip_right_tick = (bool)$skip;
+        return TRUE;
+    }
+
+    function SetXTickLength($which_xln)
     {
         $this->x_tick_length = $which_xln;
         return TRUE;
     }
 
-    function SetYTickLength($which_yln) 
+    function SetYTickLength($which_yln)
     {
         $this->y_tick_length = $which_yln;
         return TRUE;
     }
 
-    function SetXTickCrossing($which_xc) 
+    function SetXTickCrossing($which_xc)
     {
         $this->x_tick_cross = $which_xc;
         return TRUE;
     }
 
-    function SetYTickCrossing($which_yc) 
+    function SetYTickCrossing($which_yc)
     {
         $this->y_tick_cross = $which_yc;
         return TRUE;
 /////////////////////////////////////////////
 
     /*!
-     * Fills the background of the image with a solid color
+     * Fills the background.
      */
-    function DrawBackground() 
+    function DrawBackground()
     {
-        if (! $this->background_done) {     //Don't draw it twice if drawing two plots on one image
-            ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, 
-                                 $this->ndx_bg_color);
+        // Don't draw this twice if drawing two plots on one image
+        if (! $this->background_done) {
+            if (isset($this->bgimg)) {    // If bgimg is defined, use it
+                $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode);
+            } else {                        // Else use solid color
+                ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
+                                     $this->ndx_bg_color);
+            }
             $this->background_done = TRUE;
+            return TRUE;        // Done
+        }
+        return FALSE;           // Nothing done
+    }
+
+
+    /*!
+     * Fills the plot area background.
+     */
+    function DrawPlotAreaBackground()
+    {
+        if (isset($this->plotbgimg)) {
+            $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1],
+                            $this->plot_area_width, $this->plot_area_height, $this->plotbgmode);
         }
+        else {
+            if ($this->draw_plot_area_background) {
+                ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1],
+                                     $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
+            }
+        }
+
         return TRUE;
     }
 
+
+    /*!
+     * Tiles an image at some given coordinates.
+     *
+     * \param $file   string Filename of the picture to be used as tile.
+     * \param $xorig  int    X coordinate of the plot where the tile is to begin.
+     * \param $yorig  int    Y coordinate of the plot where the tile is to begin.
+     * \param $width  int    Width of the area to be tiled.
+     * \param $height int    Height of the area to be tiled.
+     * \param $mode   string One of 'centeredtile', 'tile', 'scale'.
+     */
+    function tile_img($file, $xorig, $yorig, $width, $height, $mode)
+    {
+        $size = getimagesize($file);
+        $input_format = $size[2];
+
+        switch($input_format) {
+        case 1:
+            $im = @ imagecreatefromGIF ($file);
+            if (! $im) {
+                $this->PrintError("tile_img:() Unable to open $file as a GIF.");
+                return FALSE;
+            }
+            break;
+        case 2:
+            $im = @ imagecreatefromJPEG ($file);
+            if (! $im) {
+                $this->PrintError("tile_img(): Unable to open $file as a JPG.");
+                return FALSE;
+            }
+            break;
+        case 3:
+            $im = @ imagecreatefromPNG ($file);
+            if (! $im) {
+                $this->PrintError("tile_img(): Unable to open $file as a PNG.");
+                return FALSE;
+            }
+            break;
+        default:
+            $this->PrintError('tile_img(): Please select a gif, jpg, or png image.');
+            return FALSE;
+            break;
+        }
+
+
+        if ($mode == 'scale') {
+            imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $size[0],$size[1]);
+            return TRUE;
+        } else if ($mode == 'centeredtile') {
+            $x0 = - floor($size[0]/2);   // Make the tile look better
+            $y0 = - floor($size[1]/2);
+        } else if ($mode = 'tile') {
+            $x0 = 0;
+            $y0 = 0;
+        }
+
+        // Actually draw the tile
+
+        // But first on a temporal image.
+        $tmp = ImageCreate($width, $height);
+        if (! $tmp)
+            $this->PrintError('tile_img(): Could not create image resource.');
+
+        for ($x = $x0; $x < $width; $x += $size[0])
+            for ($y = $y0; $y < $height; $y += $size[1])
+                imagecopy($tmp, $im, $x, $y, 0, 0, $size[0], $size[1]);
+
+        // Copy the temporal image onto the final one.
+        imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height);
+
+        // Free resources
+        imagedestroy($tmp);
+        imagedestroy($im);
+
+        return TRUE;
+    }  // function tile_img
+
+
     /*!
      * Draws a border around the final image.
      */
-    function DrawImageBorder() 
+    function DrawImageBorder()
     {
         switch ($this->image_border_type) {
         case 'raised':
     /*!
      * Draws the X-Axis Title
      */
-    function DrawXTitle() 
+    function DrawXTitle()
     {
         if ($this->x_title_pos == 'none')
             return;
             $ypos = $this->image_height - $this->x_title_height - $this->safe_margin;
             $this->DrawText($this->x_title_font, $this->x_title_angle,
                             $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
-        } 
+        }
         return TRUE;
     }
 
     /*!
      * Draws the Y-Axis Title
      */
-    function DrawYTitle() 
+    function DrawYTitle()
     {
         if ($this->y_title_pos == 'none')
             return;
 
         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') {
             $xpos = $this->safe_margin;
-            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, 
+            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
                             $this->y_title_txt, 'left', 'center');
         }
         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') {
             $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin;
-            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, 
+            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
                             $this->y_title_txt, 'left', 'center');
         }
 
     }
 
 
-    /*!
-     * Fills the plot area with a solid color
-     */
-    function DrawPlotAreaBackground() 
-    {
-        ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], 
-                             $this->plot_area[2], $this->plot_area[3],
-                             $this->ndx_plot_bg_color);
-        return TRUE;
-    }
-
     /*
      * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
      */
-    function DrawYAxis() 
+    function DrawYAxis()
     {
         // Draw ticks, labels and grid, if any
         $this->DrawYTicks();
 
         // Draw Y axis at X = y_axis_x_pixels
-        ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1], 
+        ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1],
                   $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color);
-                  
+
         return TRUE;
     }
 
     /*
      *
      */
-    function DrawXAxis() 
+    function DrawXAxis()
     {
         // Draw ticks, labels and grid
         $this->DrawXTicks();
 
-        //Draw Tick and Label for Y axis
-        if (! $this->skip_bottom_tick) { 
+        /* This tick and label tend to overlap with regular Y Axis labels,
+         * as Mike Pullen pointed out.
+         *
+        //Draw Tick and Label for X axis
+        if (! $this->skip_bottom_tick) {
             $ylab =$this->FormatLabel('y', $this->x_axis_position);
             $this->DrawYTick($ylab, $this->x_axis_y_pixels);
         }
-
+        */
         //Draw X Axis at Y = x_axis_y_pixels
         ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels,
                   $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);
      * Draw Just one Tick, called from DrawYTicks() and DrawXAxis()
      * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ?
      */
-    function DrawYTick($which_ylab, $which_ypix) 
+    function DrawYTick($which_ylab, $which_ypix)
     {
-        // Ticks on Y axis 
-        if ($this->y_tick_pos == 'yaxis') { 
-            ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, 
-                      $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, 
+        // Ticks on Y axis
+        if ($this->y_tick_pos == 'yaxis') {
+            ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix,
+                      $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix,
                       $this->ndx_tick_color);
         }
 
         // Labels on Y axis
         if ($this->y_tick_label_pos == 'yaxis') {
             $this->DrawText($this->y_label_font, $this->y_label_angle,
-                            $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix, 
+                            $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix,
                             $this->ndx_text_color, $which_ylab, 'right', 'center');
         }
 
         // Ticks to the left of the Plot Area
-        if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) { 
+        if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) {
             ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length,
                       $which_ypix, $this->plot_area[0] + $this->y_tick_cross,
                       $which_ypix, $this->ndx_tick_color);
         }
 
         // Ticks to the right of the Plot Area
-        if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) { 
+        if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) {
             ImageLine($this->img, ($this->plot_area[2] + $this->y_tick_length),
                       $which_ypix, $this->plot_area[2] - $this->y_tick_cross,
                       $which_ypix, $this->ndx_tick_color);
 
         // Labels to the left of the plot area
         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') {
-            $this->DrawText($this->y_label_font, $this->y_label_angle, 
-                            $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix, 
+            $this->DrawText($this->y_label_font, $this->y_label_angle,
+                            $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix,
                             $this->ndx_text_color, $which_ylab, 'right', 'center');
         }
         // Labels to the right of the plot area
         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') {
-            $this->DrawText($this->y_label_font, $this->y_label_angle, 
-                            $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix, 
+            $this->DrawText($this->y_label_font, $this->y_label_angle,
+                            $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix,
                             $this->ndx_text_color, $which_ylab, 'left', 'center');
         }
    } // Function DrawYTick()
 
     /*!
      * Draws Grid, Ticks and Tick Labels along Y-Axis
-     * Ticks and ticklabels can be left of plot only, right of plot only, 
+     * Ticks and ticklabels can be left of plot only, right of plot only,
      * both on the left and right of plot, or crossing a user defined Y-axis
+     * TODO: marks at whole numbers (-10, 10, 20, 30 ...) no matter where the plot begins (-3, 4.7, etc.)
      */
-    function DrawYTicks() 
+    function DrawYTicks()
     {
         // Sets the line style for IMG_COLOR_STYLED lines (grid)
         if ($this->dashed_grid) {
         }
 
         // maxy is always > miny so delta_y is always positive
-        if ($this->y_tick_increment) {
-            $delta_y = $this->y_tick_increment;
+        if ($this->y_tick_inc) {
+            $delta_y = $this->y_tick_inc;
         } elseif ($this->num_y_ticks) {
             $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks;
         } else {
             $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10 ;
         }
 
-        // NOTE: When working with floats, because of approximations when adding $delta_y, 
-        // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where 
+        // NOTE: When working with floats, because of approximations when adding $delta_y,
+        // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where
         // not for the substraction to $y_end here.
         $y_tmp = (double)$this->plot_min_y;
         $y_end = (double)$this->plot_max_y - ($delta_y/2);
 
     /*!
      * Draws Grid, Ticks and Tick Labels along X-Axis
-     * Ticks and tick labels can be down of plot only, up of plot only, 
+     * Ticks and tick labels can be down of plot only, up of plot only,
      * both on up and down of plot, or crossing a user defined X-axis 
      *
      * \note Original vertical code submitted by Marlin Viss
         }
 
         // Calculate x increment between ticks
-        if ($this->x_tick_increment) {
-            $delta_x = $this->x_tick_increment;
+        if ($this->x_tick_inc) {
+            $delta_x = $this->x_tick_inc;
         } elseif ($this->num_x_ticks) {
             $delta_x = ($this->plot_max_x - $this->plot_min_x) / $this->num_x_ticks;
         } else {
             $delta_x =($this->plot_max_x - $this->plot_min_x) / 10 ;
         }
 
-        // NOTE: When working with decimals, because of approximations when adding $delta_x, 
-        // $x_tmp never equals $x_end  at the for loop, so one spurious line would  get drawn where 
+        // NOTE: When working with decimals, because of approximations when adding $delta_x,
+        // $x_tmp never equals $x_end  at the for loop, so one spurious line would  get drawn where
         // not for the substraction to $x_end here.
         $x_tmp = (double)$this->plot_min_x;
         $x_end = (double)$this->plot_max_x - ($delta_x/2);
         if (! $this->skip_right_tick)
             $x_end += $delta_x;
 
-        for (;$x_tmp < $x_end; $x_tmp += $delta_x) { 
+        for (;$x_tmp < $x_end; $x_tmp += $delta_x) {
             $xlab = $this->FormatLabel('x', $x_tmp);
             $x_pixels = $this->xtr($x_tmp);
 
                 ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
             }
 
-            // Tick on X Axis 
-            if ($this->x_tick_pos == 'xaxis') { 
-                ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross, 
+            // Tick on X Axis
+            if ($this->x_tick_pos == 'xaxis') {
+
+                ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross,
                           $x_pixels, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
             }
 
             // Label on X axis
             if ($this->x_tick_label_pos == 'xaxis') {
-                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
+                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
                                 $this->x_axis_y_pixels + $this->x_tick_length*1.5, $this->ndx_text_color, 
                                 $xlab, 'center', 'bottom');
             }              
 
             // Bottom of the plot area tick label
             if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') {
-                $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
-                                $this->plot_area[3] + $this->x_tick_length*1.5, $this->ndx_text_color, 
+                $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
+                                $this->plot_area[3] + $this->x_tick_length*1.5, $this->ndx_text_color,
                                 $xlab, 'center', 'bottom');
             }
         }
     /*!
      * 
      */
-    function DrawPlotBorder() 
+    function DrawPlotBorder()
     {
         switch ($this->plot_border_type) {
         case 'left':    // for past compatibility
      * Leave the last parameter out, to avoid the drawing of vertical lines, no matter
      * what the setting is (for plots that need it, like DrawSquared())
      */
-    function DrawXDataLabel($xlab, $xpos, $row=FALSE) 
+    function DrawXDataLabel($xlab, $xpos, $row=FALSE)
     {
+        // FIXME!! not working...
+        if (($this->_x_label_cnt++ % $this->x_label_inc) != 0)
+            return;
+
         $xlab = $this->FormatLabel('x', $xlab);
-   
+
         // Labels below the plot area
         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
-            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, 
-                            $this->plot_area[3] + $this->x_tick_length, 
+            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
+                            $this->plot_area[3] + $this->x_tick_length,
                             $this->ndx_text_color, $xlab, 'center', 'bottom');
-        
+
         // Labels above the plot area
         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
-            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, 
-                            $this->plot_area[1] - $this->x_tick_length , 
+            $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
+                            $this->plot_area[1] - $this->x_tick_length ,
                             $this->ndx_text_color, $xlab, 'center', 'top');
 
         if ($row && $this->draw_x_data_label_lines)
             $this->DrawXDataLine($xpos, $row);
     }
-    
+
     /*!
      * Draws Vertical lines from data points up and down.
      * Which lines are drawn depends on the value of x_data_label_pos,
      * \param xpos int position in pixels of the line.
      * \param row int index of the data row being drawn.
      */
-    function DrawXDataLine($xpos, $row) 
+    function DrawXDataLine($xpos, $row)
     {
         // Sets the line style for IMG_COLOR_STYLED lines (grid)
         if($this->dashed_grid) {
         // Lines from the bottom up
         if ($this->x_data_label_pos == 'both') {
             ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style);
-        }         
+        }
         // Lines coming from the bottom of the plot
         else if ($this->x_data_label_pos == 'plotdown') {
             // See FindDataLimits() to see why 'MAXY' index.
      * FIXME: maximum label length should be calculated more accurately for TT fonts
      *        Performing a BBox calculation for every legend element, for example.
      */
-    function DrawLegend($which_x1, $which_y1, $which_boxtype) 
+    function DrawLegend($which_x1, $which_y1, $which_boxtype)
     {
         // Find maximum legend label length
         $max_len = 0;
 
     /*!
      * Draws a pie chart. Data has to be 'text-data' type.
-     * 
-     *  This can work in two ways: the classical, with a column for each sector 
-     *  (computes the column totals and draws the pie with that) 
+     *
+     *  This can work in two ways: the classical, with a column for each sector
+     *  (computes the column totals and draws the pie with that)
      *  OR
      *  Takes each row as a sector and uses it's first value. This has the added
      *  advantage of using the labels provided, which is not the case with the
      *  former method. This might prove useful for pie charts from GROUP BY sql queries
      */
-    function DrawPieChart() 
+    function DrawPieChart()
     {
         $xpos = $this->plot_area[0] + $this->plot_area_width/2;
         $ypos = $this->plot_area[1] + $this->plot_area_height/2;
             }
         }
         // Or only one column per row, one pie slice per row?
-        else if ($this->data_type == 'text-data-pie') {
+        else if ($this->data_type == 'text-data-single') {
             for ($i = 0; $i < $this->num_data_rows; $i++) {
                 $legend[$i] = $this->data[$i][0];                   // Set the legend to column labels
                 $sumarr[$i] = $this->data[$i][1];
                     @ $sumarr[$j] += abs($this->data[$i][$j]);
                 }
             }
-        }            
+        }
         else {
             $this->DrawError("DrawPieChart(): Data type '$this->data_type' not supported.");
             return FALSE;
                 // For shaded pies: the last one (at the top of the "stack") has a brighter color:
                 if ($h == 0)
                     $slicecol = $this->ndx_data_colors[$color_index];
-                else                
+                else
                     $slicecol = $this->ndx_data_dark_colors[$color_index];
 
                 $label_txt = number_format(($val / $total * 100), $this->y_precision, '.', ', ') . '%';
                 $end_angle += $val;
                 $mid_angle = deg2rad($end_angle - ($val / 2));
 
-                // Draw the slice       
-                ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, 
+                // Draw the slice
+                ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
                                360-$end_angle, 360-$start_angle,
                                $slicecol, IMG_ARC_PIE);
 
                 if ($h == 0) {
                     // Draw the outline
                     if (! $this->shading)
-                        ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, 
+                        ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
                                        360-$end_angle, 360-$start_angle,
                                        $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL);
 
 
-                    // The '* 1.2' trick is to get labels out of the pie chart so there are more 
+                    // The '* 1.2' trick is to get labels out of the pie chart so there are more
                     // chances they can be seen in small sectors.
                     $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
                     $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
 
                     $this->DrawText($this->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color,
-                                    $label_txt, 'center', 'center');           
-                }                                
+                                    $label_txt, 'center', 'center');
+                }
                 $color_index++;
                 $color_index = $color_index % $max_data_colors;
             }   // end for
      * Supported data formats: data-data-error, text-data-error (doesn't exist yet)
      * ( data comes in as array("title", x, y, error+, error-, y2, error2+, error2-, ...) )
      */
-    function DrawDotsError() 
+    function DrawDotsError()
     {
         $this->CheckOption($this->data_type, 'data-data-error', __FUNCTION__);
 
                 $x_now = $this->data[$row][$record++];  // Read it, advance record index
             else
                 $x_now = 0.5 + $cnt++;                  // Place text-data at X = 0.5, 1.5, 2.5, etc...
-                
+
             // Draw X Data labels?
             if ($this->x_data_label_pos != 'none')
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
             while ($record < $this->num_recs[$row]) {
                     // Y:
-                    $y_now = $this->data[$row][$record++];
-                    $this->DrawDot($x_now, $y_now, $this->point_shape, $this->ndx_data_colors[$record]);
+                    $y_now = $this->data[$row][$record];
+                    $this->DrawDot($x_now, $y_now, $record, $this->ndx_data_colors[$record++]);
 
                     // Error +
-                    $val = $this->data[$row][$record++];
+                    $val = $this->data[$row][$record];
                     $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
-                                         $this->ndx_error_bar_colors[$record]);
+                                         $this->ndx_error_bar_colors[$record++]);
                     // Error -
                     $val = $this->data[$row][$record];
                     $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
 
 
     /*
-     * Supported data types: 
+     * Supported data types:
      *  - data-data ("title", x, y1, y2, y3, ...)
      *  - text-data ("title", y1, y2, y3, ...)
      */
-    function DrawDots() 
+    function DrawDots()
     {
         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
 
 
             // Proceed with Y values
             for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
-                if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
-                    $this->DrawDot($x_now, $this->data[$row][$rec], 
-                                   $this->point_shape, $this->ndx_data_colors[$idx]);
+                if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data
+                    $this->DrawDot($x_now, $this->data[$row][$rec],
+                                   $rec, $this->ndx_data_colors[$idx]);
                 }
             }
         }
     /*!
      * A clean, fast routine for when you just want charts like stock volume charts
      */
-    function DrawThinBarLines() 
+    function DrawThinBarLines()
     {
         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
 
                     // Draws a line from user defined x axis position up to ytr($val)
                     ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels, 
                               $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]);
-                }                              
+                }
             }
         }
 
     /*!
      *
      */
-    function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) 
+    function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
     {
         /* 
         // TODO: add a parameter to show datalabels next to error bars?
      * Supported types: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
      * 'diamond', 'triangle', 'trianglemid'
      */
-    function DrawDot($x_world, $y_world, $dot_type, $color) 
+    function DrawDot($x_world, $y_world, $record, $color)
     {
-        $half_point = $this->point_size / 2;
+        // TODO: optimize, avoid counting every time we are called.
+        $record = $record % count ($this->point_shapes);
+
+        $half_point = $this->point_sizes[$record] / 2;
 
         $x_mid = $this->xtr($x_world);
         $y_mid = $this->ytr($y_world);
         $y1 = $y_mid - $half_point;
         $y2 = $y_mid + $half_point;
 
-        switch ($dot_type) {
+        switch ($this->point_shapes[$record]) {
         case 'halfline':
             ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color);
             break;
             ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
             break;
         case 'circle':
-            ImageArc($this->img, $x_mid, $y_mid, $this->point_size, $this->point_size, 0, 360, $color);
+            ImageArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360, $color);
             break;
         case 'dot':
-            ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_size, $this->point_size, 0, 360, 
+            ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360,
                            $color, IMG_ARC_PIE);
             break;
         case 'diamond':
      *      'text-data'
      *      'data-data'
      * NOTE: This function used to add first and last data values even on incomplete
-     *       sets. That is not the behaviour now. As for missing data in between, 
+     *       sets. That is not the behaviour now. As for missing data in between,
      *       there are two posibilities: replace the point with one on the X axis (previous
      *       way), or forget about it and use the preceding and following ones to draw the polygon.
      *       There is the possibility to use both, we just need to add the method to set
      *       it. Something like SetMissingDataBehaviour(), for example.
      */
-    function DrawArea() 
+    function DrawArea()
     {
         $incomplete_data_defaults_to_x_axis = FALSE;        // TODO: make this configurable
 
 
 
             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
-                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);                   
+                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
 
             // Proceed with Y values
             // Create array of points for imagefilledpolygon()
             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates.
             
 
-            if ($this->x_data_label_pos != 'none')          // Draw X Data labels? 
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
 
             // Now go for Y, E+, E-
                 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, 
                                      $this->ndx_error_bar_colors[$idx]);
 
-                // Update indices:
+                // Update indexes:
                 $start_lines[$idx] = TRUE;   // Tells us if we already drew the first column of points, 
                                              // thus having $lastx and $lasty ready for the next column.
                 $lastx[$idx] = $x_now_pixels;
         }   // end for
 
         ImageSetThickness($this->img, 1);   // Revert to original state for lines to be drawn later.
-    }   // function DrawErrorLines()
+    }   // function DrawLinesError()
 
 
 
 
                         if ($this->line_styles[$idx] == 'dashed') {
                             $this->SetDashedStyle($this->ndx_data_colors[$idx]);
-                            ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx], 
+                            ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
                                       IMG_COLOR_STYLED);
                             ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels, 
                                       IMG_COLOR_STYLED);
     /*!    
      * Data comes in as array("title", x, y, y2, y3, ...)
      */
-    function DrawBars() 
+    function DrawBars()
     {
-        if ($this->data_type != 'text-data') { 
+        if ($this->data_type != 'text-data') {
             $this->DrawError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")');
             return FALSE;
         }
 
             $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
 
-            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels? TODO:labels on top of bars.
                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
 
             // Draw the bar
             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
-                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data 
+                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data
                     $x1 = $x_now_pixels - $this->data_group_space + ($idx * $this->record_bar_width);
-                    $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width); 
+                    $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width);
 
                     if ($this->data[$row][$record] < $this->x_axis_position) {
                         $y1 = $this->x_axis_y_pixels;
                         $y2 = $this->x_axis_y_pixels;
                     }
 
+                    if ($this->shading) {                           // Draw the shade?
+                        ImageFilledPolygon($this->img, array($x1, $y1,
+                                                       $x1 + $this->shading, $y1 - $this->shading,
+                                                       $x2 + $this->shading, $y1 - $this->shading,
+                                                       $x2 + $this->shading, $y2 - $this->shading,
+                                                       $x2, $y2,
+                                                       $x2, $y1),
+                                           6, $this->ndx_data_dark_colors[$idx]);
+                    }
+                    // Or draw a border?
+                    else {
+                        ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
+                    }
+                    // Draw the bar
+                    ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
+                }
+            }   // end for
+        }   // end for
+    } //function DrawBars
+
+
+    /*!
+     * Data comes in as array("title", x, y, y2, y3, ...)
+     * \note Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net >
+     */
+    function DrawStackedBars()
+    {
+        if ($this->data_type != 'text-data') {
+            $this->DrawError('DrawStackedBars(): Bar plots must be text-data: use SetDataType("text-data")');
+            return FALSE;
+        }
+
+        for ($row = 0; $row < $this->num_data_rows; $row++) {
+            $record = 1;                                    // Skip record #0 (data label)
+
+            $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
+
+            if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+
+            // Draw the bars
+            $oldv = 0;
+            for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
+                if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data 
+                    $x1 = $x_now_pixels - $this->data_group_space;
+                    $x2 = $x_now_pixels + $this->data_group_space; 
+
+                    $y1 = $this->ytr(abs($this->data[$row][$record]) + $oldv);
+                    $y2 = $this->ytr($this->x_axis_position + $oldv);
+                    $oldv += abs($this->data[$row][$record]);
+
                     if ($this->shading) {                           // Draw the shade?
                         ImageFilledPolygon($this->img, array($x1, $y1, 
                                                        $x1 + $this->shading, $y1 - $this->shading,
                     }
                     // Draw the bar
                     ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
+                    
                 } 
             }   // end for
         }   // end for
-    } //function DrawBars    
-
+    } //function DrawStackedBars 
 
+    
     /*!
      *
      */
-    function DrawGraph() 
+    function DrawGraph()
     {
         if (! $this->img) {
             $this->DrawError('DrawGraph(): No image resource allocated');
             return FALSE;
         }
 
-        if (! isset($this->data_limits_done)) 
+        if (! isset($this->data_limits_done))
             $this->FindDataLimits();                // Get maxima and minima for scaling
 
         if ($this->total_records == 0) {            // Check for empty data sets
         if (! isset($this->plot_max_y))             // Set plot area world values (plot_max_x, etc.)
             $this->SetPlotAreaWorld();
 
-        if ($this->data_type == 'text-data')
-            $this->SetEqualXCoord();
-
-        if ($this->x_data_label_pos != 'none') {    // Default: do not draw tick stuff if 
+        if ($this->plot_type == 'bars' || $this->plot_type == 'stackedbars') // Calculate bar widths
+            $this->CalcBarWidths();
+/* FIXME!!  this sort of thing should not be done without user's consent
+        if ($this->x_data_label_pos != 'none') {    // Default: do not draw tick stuff if
             $this->x_tick_label_pos = 'none';       // there are data labels.
             $this->x_tick_pos = 'none';
         }
-
+*/
         $this->PadArrays();                         // Pad color and style arrays to fit records per group.
 
         $this->DrawBackground();
 
         $this->DrawImageBorder();
 
-        if ($this->draw_plot_area_background)
-            $this->DrawPlotAreaBackground();
+        $this->DrawPlotAreaBackground();
 
         $this->DrawTitle();
         $this->DrawXTitle();
         $this->DrawYTitle();
 
+        // Pie charts are drawn differently, handle them first
+        if ($this->plot_type == 'pie') {
+            // Pie charts can maximize image space usage.
+            $this->SetPlotAreaPixels($this->safe_margin, $this->title_height,
+                                     $this->image_width - $this->safe_margin,
+                                     $this->image_height - $this->safe_margin);
+            $this->DrawPieChart();
+
+            if ($this->legend)
+                $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
+
+            if ($this->print_image)
+                $this->PrintImage();
+                
+            return;
+        }
+
+        ////// All other chart types:
+        
+        if (! $this->grid_at_foreground) {         // Usually one wants grids to go back, but...
+            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis()) 
+            $this->DrawXAxis();
+        }
+
         switch ($this->plot_type) {
         case 'thinbarline':
             $this->DrawThinBarLines();
                 $this->DrawDots();
             }
             break;
-        case 'pie':
-            // Pie charts can maximize image space usage.
-            $this->SetPlotAreaPixels($this->safe_margin, $this->title_height,
-                                     $this->image_width - $this->safe_margin,
-                                     $this->image_height - $this->safe_margin);
-            $this->DrawPieChart();
-            break;
+        case 'stackedbars':
+            $this->DrawStackedBars();
+            break; 
         case 'bars':
+            $this->DrawBars();
+            break;
         default:
             $this->plot_type = 'bars';  // Set it if it wasn't already set.
-            $this->DrawYAxis();     // We don't want the grid to overwrite bar charts
-            $this->DrawXAxis();     // so we draw it first. Also, Y must be drawn before X (see DrawYAxis())
             $this->DrawBars();
-            $this->DrawPlotBorder();
             break;
         }   // end switch
 
-        if ($this->plot_type != 'pie' && $this->plot_type != 'bars') {
-            $this->DrawYAxis();
+        if ($this->grid_at_foreground) {         // Usually one wants grids to go back, but...
+            $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis()) 
             $this->DrawXAxis();
-            $this->DrawPlotBorder();
         }
+
+        $this->DrawPlotBorder();
+        
         if ($this->legend)
             $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
 
      */
     function SetHorizTickPosition($which_tp) 
     { 
-        return $this->SetXTickPos($which_tp); 
+        return $this->SetXTickPos($which_tp);
     }
 
     /*!
     /*!
      * \deprecated Use SetFont()
      */
-    function SetXLabelFontSize($which_size) 
+    function SetXLabelFontSize($which_size)
     {
         return $this->SetFont('x_title', $which_size);
     }
 
     /*!
      * \deprecated Use SetXTitle()
-     */ 
+     */
     function SetXLabel($which_xlab) 
     {
         return $this->SetXTitle($which_xlab);
     /*!
      * \deprecated Use SetXLabelAngle()
      */
-    function SetXDataLabelAngle($which_xdla) 
+    function SetXDataLabelAngle($which_xdla)
     {
         return $this->SetXLabelAngle($which_xdla);
     }
      *
      * \deprecated Use SetXDataLabelPos()
      */
-    function SetDrawXDataLabels($which_dxdl) 
+    function SetDrawXDataLabels($which_dxdl)
     {
         if ($which_dxdl == '1' )
             $this->SetXDataLabelPos('plotdown');
     }
 
     /*!
-     * \deprecated Calculates maximum X-Axis label height. Now inside _CalcMargins()
+     * \deprecated Calculates maximum X-Axis label height. Now inside CalcMargins()
      */
     function CalcXHeights() 
     {
         else { // For Non-TTF fonts we can have only angles 0 or 90
             if ($this->x_label_angle == 90)
                 $this->x_tick_label_height = $this->max_t * $this->x_label_font['width'];
-            else 
+            else
                 $this->x_tick_label_height = $this->x_label_font['height'];
         }
 
      * Set up the image resource 'img'
      * \deprecated The constructor should init 'img'
      */
-    function InitImage() 
+    function InitImage()
     {
         $this->img = ImageCreate($this->image_width, $this->image_height);
 
     /*!
      * \deprecated Use _SetRGBColor()
      */
-    function SetColor($which_color) 
-    { 
+    function SetColor($which_color)
+    {
         $this->SetRGBColor($which_color);
         return TRUE;
     }
 
-    /* 
+    /*
      * \deprecated Use SetLineWidths().
      */
-    function SetLineWidth($which_lw) 
+    function SetLineWidth($which_lw)
     {
 
         $this->SetLineWidths($which_lw);
 
-        if (!$this->error_bar_line_width) { 
+        if (!$this->error_bar_line_width) {
             $this->SetErrorBarLineWidth($which_lw);
         }
         return TRUE;
     }
 
     /*!
-     * \deprecated 
+     * \deprecated
      */
     function DrawDashedLine($x1, $y1, $x2, $y2 , $dash_length, $dash_space, $color)
     {
         $style = array_merge($dashes, $spaces);
         ImageSetStyle($this->img, $style);
         ImageLine($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
-    }        
+    }
+
+    /*!
+     * \deprecated Selects an input file to be used as background for the whole graph.
+     * This resizes the graph to the image's size.
+     */
+    function SetInputFile($which_input_file)
+    {
+        $size = GetImageSize($which_input_file);
+        $input_type = $size[2];
+
+        switch($input_type) {
+        case 1:
+            $im = @ ImageCreateFromGIF ($which_input_file);
+            if (!$im) { // See if it failed
+                $this->PrintError("Unable to open $which_input_file as a GIF");
+                return FALSE;
+            }
+        break;
+        case 3:
+            $im = @ ImageCreateFromPNG ($which_input_file);
+            if (!$im) { // See if it failed
+                $this->PrintError("Unable to open $which_input_file as a PNG");
+                return FALSE;
+            }
+        break;
+        case 2:
+            $im = @ ImageCreateFromJPEG ($which_input_file);
+            if (!$im) { // See if it failed
+                $this->PrintError("Unable to open $which_input_file as a JPG");
+                return FALSE;
+            }
+        break;
+        default:
+            $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!');
+            return FALSE;
+        break;
+        }
+
+        // Set Width and Height of Image
+        $this->image_width = $size[0];
+        $this->image_height = $size[1];
+
+        // Deallocate any resources previously allocated
+        if ($this->img)
+            imagedestroy($this->img);
+
+        $this->img = $im;
+
+        return TRUE;
+
+    }
+
+
+    /*
+     * \deprecated Use SetPointShapes().
+     */
+    function SetPointShape($which_pt)
+    {
+        $this->SetPointShapes($which_pt);
+        return TRUE;
+    }
+
+    /*
+     * \deprecated Use SetPointSizes().
+     */
+    function SetPointSize($which_ps)
+    {
+        $this->SetPointSizes($which_ps);
+        return TRUE;
+    }
 }  // class PHPlot
 
 
 
-//////////////////////// 
+////////////////////////
 
 
 /*!
         $arr2 = $arr;                           // copy the original array
     }
     while (count($arr) < $size)
-        $arr = array_merge($arr, $arr2);        // append until done
+        $arr = array_merge_php4($arr, $arr2);        // append until done
 }
+
+/*!
+ * Fixes problem with array_merge() in PHP5.
+ * \note I simply copied this from a bug report. I am not running php5 yet, so
+ *       I cannot reproduce it, which is why I trust the reporter.
+ */
+function array_merge_php4($array1,$array2)
+{
+    $return=array();
+
+    foreach(func_get_args() as $arg){
+        if(!is_array($arg)){
+        $arg=array($arg);
+        }
+        foreach($arg as $key=>$val){
+            if(!is_int($key)){
+                $return[$key]=$val;
+            }else{
+                $return[]=$val;
+            }
+        }
+    }
+    return $return;
+ }
+ 
+ 
+
+
 ?>