| <?php |
| //======================================================================= |
| // File: JPGRAPH.PHP |
| // Description: PHP Graph Plotting library. Base module. |
| // Created: 2001-01-08 |
| // Author: Johan Persson (johanp@aditus.nu) |
| // Ver: $Id$ |
| // |
| // Copyright (c) Aditus Consulting. All rights reserved. |
| //======================================================================== |
| |
| require_once('jpg-config.inc'); |
| |
| require_once('gd_image.inc.php'); |
| |
| // Version info |
| DEFINE('JPG_VERSION','1.20.3'); |
| |
| // Minimum required PHP version |
| DEFINE('MIN_PHPVERSION','4.3.1'); |
| |
| // For internal use only |
| DEFINE("_JPG_DEBUG",false); |
| DEFINE("_FORCE_IMGTOFILE",false); |
| DEFINE("_FORCE_IMGDIR",'/tmp/jpgimg/'); |
| |
| //------------------------------------------------------------------------ |
| // Automatic settings of path for cache and font directory |
| // if they have not been previously specified |
| //------------------------------------------------------------------------ |
| if(USE_CACHE) { |
| if (!defined('CACHE_DIR')) { |
| if ( strstr( PHP_OS, 'WIN') ) { |
| if( empty($_SERVER['TEMP']) ) { |
| $t = new ErrMsgText(); |
| $msg = $t->Get(11,$file,$lineno); |
| die($msg); |
| } |
| else { |
| DEFINE('CACHE_DIR', $_SERVER['TEMP'] . '/'); |
| } |
| } else { |
| DEFINE('CACHE_DIR','/tmp/jpgraph_cache/'); |
| } |
| } |
| } |
| elseif( !defined('CACHE_DIR') ) { |
| DEFINE('CACHE_DIR', ''); |
| } |
| |
| if (!defined('TTF_DIR')) { |
| if (strstr( PHP_OS, 'WIN') ) { |
| $sroot = getenv('SystemRoot'); |
| if( empty($sroot) ) { |
| $t = new ErrMsgText(); |
| $msg = $t->Get(12,$file,$lineno); |
| die($msg); |
| } |
| else { |
| DEFINE('TTF_DIR', $sroot.'/fonts/'); |
| } |
| } else { |
| DEFINE('TTF_DIR','/usr/X11R6/lib/X11/fonts/truetype/'); |
| } |
| } |
| |
| //------------------------------------------------------------------ |
| // Constants which are used as parameters for the method calls |
| //------------------------------------------------------------------ |
| |
| // Tick density |
| DEFINE("TICKD_DENSE",1); |
| DEFINE("TICKD_NORMAL",2); |
| DEFINE("TICKD_SPARSE",3); |
| DEFINE("TICKD_VERYSPARSE",4); |
| |
| // Side for ticks and labels. |
| DEFINE("SIDE_LEFT",-1); |
| DEFINE("SIDE_RIGHT",1); |
| DEFINE("SIDE_DOWN",-1); |
| DEFINE("SIDE_BOTTOM",-1); |
| DEFINE("SIDE_UP",1); |
| DEFINE("SIDE_TOP",1); |
| |
| // Legend type stacked vertical or horizontal |
| DEFINE("LEGEND_VERT",0); |
| DEFINE("LEGEND_HOR",1); |
| |
| // Mark types for plot marks |
| DEFINE("MARK_SQUARE",1); |
| DEFINE("MARK_UTRIANGLE",2); |
| DEFINE("MARK_DTRIANGLE",3); |
| DEFINE("MARK_DIAMOND",4); |
| DEFINE("MARK_CIRCLE",5); |
| DEFINE("MARK_FILLEDCIRCLE",6); |
| DEFINE("MARK_CROSS",7); |
| DEFINE("MARK_STAR",8); |
| DEFINE("MARK_X",9); |
| DEFINE("MARK_LEFTTRIANGLE",10); |
| DEFINE("MARK_RIGHTTRIANGLE",11); |
| DEFINE("MARK_FLASH",12); |
| DEFINE("MARK_IMG",13); |
| DEFINE("MARK_FLAG1",14); |
| DEFINE("MARK_FLAG2",15); |
| DEFINE("MARK_FLAG3",16); |
| DEFINE("MARK_FLAG4",17); |
| |
| // Builtin images |
| DEFINE("MARK_IMG_PUSHPIN",50); |
| DEFINE("MARK_IMG_SPUSHPIN",50); |
| DEFINE("MARK_IMG_LPUSHPIN",51); |
| DEFINE("MARK_IMG_DIAMOND",52); |
| DEFINE("MARK_IMG_SQUARE",53); |
| DEFINE("MARK_IMG_STAR",54); |
| DEFINE("MARK_IMG_BALL",55); |
| DEFINE("MARK_IMG_SBALL",55); |
| DEFINE("MARK_IMG_MBALL",56); |
| DEFINE("MARK_IMG_LBALL",57); |
| DEFINE("MARK_IMG_BEVEL",58); |
| |
| // Inline defines |
| DEFINE("INLINE_YES",1); |
| DEFINE("INLINE_NO",0); |
| |
| // Format for background images |
| DEFINE("BGIMG_FILLPLOT",1); |
| DEFINE("BGIMG_FILLFRAME",2); |
| DEFINE("BGIMG_COPY",3); |
| DEFINE("BGIMG_CENTER",4); |
| |
| // Depth of objects |
| DEFINE("DEPTH_BACK",0); |
| DEFINE("DEPTH_FRONT",1); |
| |
| // Direction |
| DEFINE("VERTICAL",1); |
| DEFINE("HORIZONTAL",0); |
| |
| |
| // Axis styles for scientific style axis |
| DEFINE('AXSTYLE_SIMPLE',1); |
| DEFINE('AXSTYLE_BOXIN',2); |
| DEFINE('AXSTYLE_BOXOUT',3); |
| DEFINE('AXSTYLE_YBOXIN',4); |
| DEFINE('AXSTYLE_YBOXOUT',5); |
| |
| // Style for title backgrounds |
| DEFINE('TITLEBKG_STYLE1',1); |
| DEFINE('TITLEBKG_STYLE2',2); |
| DEFINE('TITLEBKG_STYLE3',3); |
| DEFINE('TITLEBKG_FRAME_NONE',0); |
| DEFINE('TITLEBKG_FRAME_FULL',1); |
| DEFINE('TITLEBKG_FRAME_BOTTOM',2); |
| DEFINE('TITLEBKG_FRAME_BEVEL',3); |
| DEFINE('TITLEBKG_FILLSTYLE_HSTRIPED',1); |
| DEFINE('TITLEBKG_FILLSTYLE_VSTRIPED',2); |
| DEFINE('TITLEBKG_FILLSTYLE_SOLID',3); |
| |
| // Style for background gradient fills |
| DEFINE('BGRAD_FRAME',1); |
| DEFINE('BGRAD_MARGIN',2); |
| DEFINE('BGRAD_PLOT',3); |
| |
| // Width of tab titles |
| DEFINE('TABTITLE_WIDTHFIT',0); |
| DEFINE('TABTITLE_WIDTHFULL',-1); |
| |
| // Defines for 3D skew directions |
| DEFINE('SKEW3D_UP',0); |
| DEFINE('SKEW3D_DOWN',1); |
| DEFINE('SKEW3D_LEFT',2); |
| DEFINE('SKEW3D_RIGHT',3); |
| |
| |
| |
| // |
| // Get hold of gradient class (In Version 2.x) |
| // A client of the library has to manually include this |
| // |
| require_once 'jpgraph_gradient.php'; |
| |
| GLOBAL $__jpg_err; |
| GLOBAL $__jpg_err_locale ; |
| $__jpg_err_locale = DEFAULT_ERR_LOCALE; |
| |
| class ErrMsgText { |
| var $lt=NULL; |
| function ErrMsgText() { |
| GLOBAL $__jpg_err_locale; |
| $file = 'lang/'.$__jpg_err_locale.'.inc.php'; |
| |
| // If the chosen locale doesn't exist try english |
| if( !file_exists(dirname(__FILE__).'/'.$file) ) { |
| $__jpg_err_locale = 'en'; |
| } |
| |
| $file = 'lang/'.$__jpg_err_locale.'.inc.php'; |
| if( !file_exists(dirname(__FILE__).'/'.$file) ) { |
| die('Internal error: Chosen locale file for error messages does not exist.'); |
| } |
| require_once($file); |
| $this->lt = $_jpg_messages; |
| } |
| |
| function Get($errnbr,$a1=null,$a2=null,$a3=null,$a4=null,$a5=null) { |
| GLOBAL $__jpg_err_locale; |
| if( !isset($this->lt[$errnbr]) ) { |
| return 'Internal error: The specified error message ('.$errnbr.') does not exist in the chosen locale ('.$__jpg_err_locale.')'; |
| } |
| $ea = $this->lt[$errnbr]; |
| $j=0; |
| if( $a1 !== null ) { |
| $argv[$j++] = $a1; |
| if( $a2 !== null ) { |
| $argv[$j++] = $a2; |
| if( $a3 !== null ) { |
| $argv[$j++] = $a3; |
| if( $a4 !== null ) { |
| $argv[$j++] = $a4; |
| if( $a5 !== null ) { |
| $argv[$j++] = $a5; |
| } |
| } |
| } |
| } |
| } |
| $numargs = $j; |
| if( $ea[1] != $numargs ) { |
| // Error message argument count do not match. |
| // Just return the error message without arguments. |
| return $ea[0]; |
| } |
| switch( $numargs ) { |
| case 1: |
| $msg = sprintf($ea[0],$argv[0]); |
| break; |
| case 2: |
| $msg = sprintf($ea[0],$argv[0],$argv[1]); |
| break; |
| case 3: |
| $msg = sprintf($ea[0],$argv[0],$argv[1],$argv[2]); |
| break; |
| case 4: |
| $msg = sprintf($ea[0],$argv[0],$argv[1],$argv[2],$argv[3]); |
| break; |
| case 5: |
| $msg = sprintf($ea[0],$argv[0],$argv[1],$argv[2],$argv[3],$argv[4]); |
| break; |
| case 0: |
| default: |
| $msg = sprintf($ea[0]); |
| break; |
| } |
| return $msg; |
| } |
| } |
| |
| // |
| // A wrapper class that is used to access the specified error object |
| // (to hide the global error parameter and avoid having a GLOBAL directive |
| // in all methods. |
| // |
| class JpGraphError { |
| function Install($aErrObject) { |
| GLOBAL $__jpg_err; |
| $__jpg_err = $aErrObject; |
| } |
| function SetErrLocale($aLoc) { |
| GLOBAL $__jpg_err_locale ; |
| $__jpg_err_locale = $aLoc; |
| } |
| function Raise($aMsg,$aHalt=true){ |
| GLOBAL $__jpg_err; |
| $tmp = new $__jpg_err; |
| $tmp->Raise($aMsg,$aHalt); |
| } |
| function RaiseL($errnbr,$a1=null,$a2=null,$a3=null,$a4=null,$a5=null) { |
| GLOBAL $__jpg_err; |
| $t = new ErrMsgText(); |
| $msg = $t->Get($errnbr,$a1,$a2,$a3,$a4,$a5); |
| $tmp = new $__jpg_err; |
| $tmp->Raise($msg); |
| } |
| } |
| |
| // |
| // ... and install the default error handler |
| // |
| if( USE_IMAGE_ERROR_HANDLER ) { |
| $__jpg_err = "JpGraphErrObjectImg"; |
| } |
| else { |
| $__jpg_err = "JpGraphErrObject"; |
| } |
| |
| function CheckPHPVersion($aMinVersion) |
| { |
| list($majorC, $minorC, $editC) = split('[/.-]', PHP_VERSION); |
| list($majorR, $minorR, $editR) = split('[/.-]', $aMinVersion); |
| |
| if ($majorC > $majorR) return true; |
| if ($majorC < $majorR) return false; |
| // same major - check ninor |
| if ($minorC > $minorR) return true; |
| if ($minorC < $minorR) return false; |
| // and same minor |
| if ($editC >= $editR) return true; |
| return true; |
| } |
| |
| // |
| // Make GD sanity check |
| // |
| if( !function_exists("imagetypes") || !function_exists('imagecreatefromstring') ) { |
| JpGraphError::RaiseL(25001); |
| //("This PHP installation is not configured with the GD library. Please recompile PHP with GD support to run JpGraph. (Neither function imagetypes() nor imagecreatefromstring() does exist)"); |
| } |
| |
| |
| // |
| // Routine to determine if GD1 or GD2 is installed |
| // |
| function CheckGDVersion() { |
| $GDfuncList = get_extension_funcs('gd'); |
| if( !$GDfuncList ) return 0 ; |
| else { |
| if( in_array('imagegd2',$GDfuncList) && |
| in_array('imagecreatetruecolor',$GDfuncList)) |
| return 2; |
| else |
| return 1; |
| } |
| } |
| |
| // |
| // Check what version of the GD library is installed. |
| // |
| $GLOBALS['gd2'] = false; |
| if( USE_LIBRARY_GD2 === 'auto' ) { |
| $gdversion = CheckGDVersion(); |
| if( $gdversion == 2 ) { |
| $GLOBALS['gd2'] = true; |
| $GLOBALS['copyfunc'] = 'imagecopyresampled'; |
| } |
| elseif( $gdversion == 1 ) { |
| $GLOBALS['gd2'] = false; |
| $GLOBALS['copyfunc'] = 'imagecopyresized'; |
| } |
| else { |
| JpGraphError::RaiseL(25002); |
| //(" Your PHP installation does not seem to have the required GD library. Please see the PHP documentation on how to install and enable the GD library."); |
| } |
| } |
| else { |
| $GLOBALS['gd2'] = USE_LIBRARY_GD2; |
| $GLOBALS['copyfunc'] = USE_LIBRARY_GD2 ? 'imagecopyresampled' : 'imagecopyresized'; |
| } |
| |
| // |
| // Make sure PHP version is high enough |
| // |
| if( !CheckPHPVersion(MIN_PHPVERSION) ) { |
| JpGraphError::RaiseL(13,PHP_VERSION,MIN_PHPVERSION); |
| } |
| |
| // |
| // First of all set up a default error handler |
| // |
| |
| |
| //============================================================= |
| // The default trivial text error handler. |
| //============================================================= |
| class JpGraphErrObject { |
| |
| var $iTitle = "JpGraph Error"; |
| var $iDest = false; |
| |
| function JpGraphErrObject() { |
| // Empty. Reserved for future use |
| } |
| |
| function SetTitle($aTitle) { |
| $this->iTitle = $aTitle; |
| } |
| |
| function SetStrokeDest($aDest) { |
| $this->iDest = $aDest; |
| } |
| |
| // If aHalt is true then execution can't continue. Typical used for fatal errors. |
| function Raise($aMsg,$aHalt=true) { |
| $aMsg = $this->iTitle.' '.$aMsg; |
| if ($this->iDest) { |
| $f = @fopen($this->iDest,'a'); |
| if( $f ) { |
| @fwrite($f,$aMsg); |
| @fclose($f); |
| } |
| } |
| else { |
| echo $aMsg; |
| } |
| if( $aHalt ) |
| die(); |
| } |
| } |
| |
| //============================================================== |
| // An image based error handler |
| //============================================================== |
| class JpGraphErrObjectImg extends JpGraphErrObject { |
| |
| function Raise($aMsg,$aHalt=true) { |
| $img_iconerror = |
| 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaV'. |
| 'BMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'. |
| 'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpY'. |
| 'iYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'. |
| 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'. |
| 'IAAAsSAdLdfvwAAAAHdElNRQfTBgISOCqusfs5AAABLUlEQVR4'. |
| '2tWV3XKCMBBGWfkranCIVClKLd/7P2Q3QsgCxjDTq+6FE2cPH+'. |
| 'xJ0Ogn2lQbsT+Wrs+buAZAV4W5T6Bs0YXBBwpKgEuIu+JERAX6'. |
| 'wM2rHjmDdEITmsQEEmWADgZm6rAjhXsoMGY9B/NZBwJzBvn+e3'. |
| 'wHntCAJdGu9SviwIwoZVDxPB9+Rc0TSEbQr0j3SA1gwdSn6Db0'. |
| '6Tm1KfV6yzWGQO7zdpvyKLKBDmRFjzeB3LYgK7r6A/noDAfjtS'. |
| 'IXaIzbJSv6WgUebTMV4EoRB8a2mQiQjgtF91HdKDKZ1gtFtQjk'. |
| 'YcWaR5OKOhkYt+ZsTFdJRfPAApOpQYJTNHvCRSJR6SJngQadfc'. |
| 'vd69OLMddVOPCGVnmrFD8bVYd3JXfxXPtLR/+mtv59/ALWiiMx'. |
| 'qL72fwAAAABJRU5ErkJggg==' ; |
| |
| if( function_exists("imagetypes") ) |
| $supported = imagetypes(); |
| else |
| $supported = 0; |
| |
| if( !function_exists('imagecreatefromstring') ) |
| $supported = 0; |
| |
| if( ob_get_length() || headers_sent() || !($supported & IMG_PNG) ) { |
| // Special case for headers already sent or that the installation doesn't support |
| // the PNG format (which the error icon is encoded in). |
| // Dont return an image since it can't be displayed |
| die($this->iTitle.' '.$aMsg); |
| } |
| |
| $aMsg = wordwrap($aMsg,55); |
| $lines = substr_count($aMsg,"\n"); |
| |
| // Create the error icon GD |
| $erricon = Image::CreateFromString(base64_decode($img_iconerror)); |
| |
| // Create an image that contains the error text. |
| $w=400; |
| $h=100 + 15*max(0,$lines-3); |
| |
| $img = new Image($w,$h); |
| |
| // Drop shadow |
| $img->SetColor("gray"); |
| $img->FilledRectangle(5,5,$w-1,$h-1,10); |
| $img->SetColor("gray:0.7"); |
| $img->FilledRectangle(5,5,$w-3,$h-3,10); |
| |
| // Window background |
| $img->SetColor("lightblue"); |
| $img->FilledRectangle(1,1,$w-5,$h-5); |
| $img->CopyCanvasH($img->img,$erricon,5,30,0,0,40,40); |
| |
| // Window border |
| $img->SetColor("black"); |
| $img->Rectangle(1,1,$w-5,$h-5); |
| $img->Rectangle(0,0,$w-4,$h-4); |
| |
| // Window top row |
| $img->SetColor("darkred"); |
| for($y=3; $y < 18; $y += 2 ) |
| $img->Line(1,$y,$w-6,$y); |
| |
| // "White shadow" |
| $img->SetColor("white"); |
| |
| // Left window edge |
| $img->Line(2,2,2,$h-5); |
| $img->Line(2,2,$w-6,2); |
| |
| // "Gray button shadow" |
| $img->SetColor("darkgray"); |
| |
| // Gray window shadow |
| $img->Line(2,$h-6,$w-5,$h-6); |
| $img->Line(3,$h-7,$w-5,$h-7); |
| |
| // Window title |
| $m = floor($w/2-5); |
| $l = 100; |
| $img->SetColor("lightgray:1.3"); |
| $img->FilledRectangle($m-$l,2,$m+$l,16); |
| |
| // Stroke text |
| $img->SetColor("darkred"); |
| $img->SetFont(FF_FONT2,FS_BOLD); |
| $img->StrokeText($m-50,15,$this->iTitle); |
| $img->SetColor("black"); |
| $img->SetFont(FF_FONT1,FS_NORMAL); |
| $txt = new Text($aMsg,52,25); |
| $txt->Align("left","top"); |
| $txt->Stroke($img); |
| if ($this->iDest) { |
| $img->Stream($this->iDest); |
| } else { |
| $img->Headers(); |
| $img->Stream(); |
| } |
| if( $aHalt ) |
| die(); |
| } |
| } |
| |
| // |
| // Setup PHP error handler |
| // |
| function _phpErrorHandler($errno,$errmsg,$filename, $linenum, $vars) { |
| // Respect current error level |
| if( $errno & error_reporting() ) { |
| JpGraphError::RaiseL(25003,basename($filename),$linenum,$errmsg); |
| } |
| } |
| |
| if( INSTALL_PHP_ERR_HANDLER ) { |
| set_error_handler("_phpErrorHandler"); |
| } |
| |
| // |
| //Check if there were any warnings, perhaps some wrong includes by the user |
| // |
| if( isset($GLOBALS['php_errormsg']) && CATCH_PHPERRMSG && |
| !preg_match('|Deprecated|', $GLOBALS['php_errormsg'])) { |
| JpGraphError::RaiseL(25004,$GLOBALS['php_errormsg']); |
| } |
| |
| |
| // Useful mathematical function |
| function sign($a) {return $a >= 0 ? 1 : -1;} |
| |
| // Utility function to generate an image name based on the filename we |
| // are running from and assuming we use auto detection of graphic format |
| // (top level), i.e it is safe to call this function |
| // from a script that uses JpGraph |
| function GenImgName() { |
| global $_SERVER; |
| |
| // Determine what format we should use when we save the images |
| $supported = imagetypes(); |
| if( $supported & IMG_PNG ) $img_format="png"; |
| elseif( $supported & IMG_GIF ) $img_format="gif"; |
| elseif( $supported & IMG_JPG ) $img_format="jpeg"; |
| |
| if( !isset($_SERVER['PHP_SELF']) ) |
| JpGraphError::RaiseL(25005); |
| //(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line if you want to use the 'auto' naming of cache or image files."); |
| $fname = basename($_SERVER['PHP_SELF']); |
| if( !empty($_SERVER['QUERY_STRING']) ) { |
| $q = @$_SERVER['QUERY_STRING']; |
| $fname .= '?'.preg_replace("/\W/", "_", $q).'.'.$img_format; |
| } |
| else { |
| $fname = substr($fname,0,strlen($fname)-4).'.'.$img_format; |
| } |
| return $fname; |
| } |
| |
| class LanguageConv { |
| var $g2312 = null ; |
| |
| function Convert($aTxt,$aFF) { |
| if( LANGUAGE_GREEK ) { |
| if( GREEK_FROM_WINDOWS ) { |
| $unistring = LanguageConv::gr_win2uni($aTxt); |
| } else { |
| $unistring = LanguageConv::gr_iso2uni($aTxt); |
| } |
| return $unistring; |
| } elseif( LANGUAGE_CYRILLIC ) { |
| if( CYRILLIC_FROM_WINDOWS && (!defined('LANGUAGE_CHARSET') || stristr(LANGUAGE_CHARSET, 'windows-1251')) ) { |
| $aTxt = convert_cyr_string($aTxt, "w", "k"); |
| } |
| if( !defined('LANGUAGE_CHARSET') || stristr(LANGUAGE_CHARSET, 'koi8-r') || stristr(LANGUAGE_CHARSET, 'windows-1251')) { |
| $isostring = convert_cyr_string($aTxt, "k", "i"); |
| $unistring = LanguageConv::iso2uni($isostring); |
| } |
| else { |
| $unistring = $aTxt; |
| } |
| return $unistring; |
| } |
| elseif( $aFF === FF_SIMSUN ) { |
| // Do Chinese conversion |
| if( $this->g2312 == null ) { |
| include_once 'jpgraph_gb2312.php' ; |
| $this->g2312 = new GB2312toUTF8(); |
| } |
| return $this->g2312->gb2utf8($aTxt); |
| } |
| elseif( $aFF === FF_CHINESE ) { |
| if( !function_exists('iconv') ) { |
| JpGraphError::RaiseL(25006); |
| //('Usage of FF_CHINESE (FF_BIG5) font family requires that your PHP setup has the iconv() function. By default this is not compiled into PHP (needs the "--width-iconv" when configured).'); |
| } |
| return iconv('BIG5','UTF-8',$aTxt); |
| } |
| elseif( ASSUME_EUCJP_ENCODING && |
| ($aFF == FF_MINCHO || $aFF == FF_GOTHIC || $aFF == FF_PMINCHO || $aFF == FF_PGOTHIC) ) { |
| if( !function_exists('mb_convert_encoding') ) { |
| JpGraphError::RaiseL(25127); |
| } |
| return mb_convert_encoding($aTxt, 'UTF-8','EUC-JP'); |
| } |
| else |
| return $aTxt; |
| } |
| |
| // Translate iso encoding to unicode |
| function iso2uni ($isoline){ |
| $uniline=''; |
| for ($i=0; $i < strlen($isoline); $i++){ |
| $thischar=substr($isoline,$i,1); |
| $charcode=ord($thischar); |
| $uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar; |
| } |
| return $uniline; |
| } |
| |
| // Translate greek iso encoding to unicode |
| function gr_iso2uni ($isoline) { |
| $uniline=''; |
| for ($i=0; $i < strlen($isoline); $i++) { |
| $thischar=substr($isoline,$i,1); |
| $charcode=ord($thischar); |
| $uniline.=($charcode>179 && $charcode!=183 && $charcode!=187 && $charcode!=189) ? "&#" . (900+($charcode-180)). ";" : $thischar; |
| } |
| return $uniline; |
| } |
| |
| // Translate greek win encoding to unicode |
| function gr_win2uni ($winline) { |
| $uniline=''; |
| for ($i=0; $i < strlen($winline); $i++) { |
| $thischar=substr($winline,$i,1); |
| $charcode=ord($thischar); |
| if ($charcode==161 || $charcode==162) { |
| $uniline.="&#" . (740+$charcode). ";"; |
| } |
| else { |
| $uniline.=(($charcode>183 && $charcode!=187 && $charcode!=189) || $charcode==180) ? "&#" . (900+($charcode-180)). ";" : $thischar; |
| } |
| } |
| return $uniline; |
| } |
| } |
| |
| //=================================================== |
| // CLASS JpgTimer |
| // Description: General timing utility class to handle |
| // time measurement of generating graphs. Multiple |
| // timers can be started. |
| //=================================================== |
| class JpgTimer { |
| var $start; |
| var $idx; |
| //--------------- |
| // CONSTRUCTOR |
| function JpgTimer() { |
| $this->idx=0; |
| } |
| |
| //--------------- |
| // PUBLIC METHODS |
| |
| // Push a new timer start on stack |
| function Push() { |
| list($ms,$s)=explode(" ",microtime()); |
| $this->start[$this->idx++]=floor($ms*1000) + 1000*$s; |
| } |
| |
| // Pop the latest timer start and return the diff with the |
| // current time |
| function Pop() { |
| assert($this->idx>0); |
| list($ms,$s)=explode(" ",microtime()); |
| $etime=floor($ms*1000) + (1000*$s); |
| $this->idx--; |
| return $etime-$this->start[$this->idx]; |
| } |
| } // Class |
| |
| $gJpgBrandTiming = BRAND_TIMING; |
| //=================================================== |
| // CLASS DateLocale |
| // Description: Hold localized text used in dates |
| //=================================================== |
| class DateLocale { |
| |
| var $iLocale = 'C'; // environmental locale be used by default |
| |
| var $iDayAbb = null; |
| var $iShortDay = null; |
| var $iShortMonth = null; |
| var $iMonthName = null; |
| |
| //--------------- |
| // CONSTRUCTOR |
| function DateLocale() { |
| settype($this->iDayAbb, 'array'); |
| settype($this->iShortDay, 'array'); |
| settype($this->iShortMonth, 'array'); |
| settype($this->iMonthName, 'array'); |
| |
| |
| $this->Set('C'); |
| } |
| |
| //--------------- |
| // PUBLIC METHODS |
| function Set($aLocale) { |
| if ( in_array($aLocale, array_keys($this->iDayAbb)) ){ |
| $this->iLocale = $aLocale; |
| return TRUE; // already cached nothing else to do! |
| } |
| |
| $pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME |
| $res = @setlocale(LC_TIME, $aLocale); |
| if ( ! $res ){ |
| JpGraphError::RaiseL(25007,$aLocale); |
| //("You are trying to use the locale ($aLocale) which your PHP installation does not support. Hint: Use '' to indicate the default locale for this geographic region."); |
| return FALSE; |
| } |
| |
| $this->iLocale = $aLocale; |
| |
| for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){ |
| $day = strftime('%a', strtotime("$ofs day")); |
| $day{0} = strtoupper($day{0}); |
| $this->iDayAbb[$aLocale][]= $day{0}; |
| $this->iShortDay[$aLocale][]= $day; |
| } |
| |
| for($i=1; $i<=12; ++$i) { |
| list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01"))); |
| $this->iShortMonth[$aLocale][] = ucfirst($short); |
| $this->iMonthName [$aLocale][] = ucfirst($full); |
| } |
| |
| // Return to original locale |
| setlocale(LC_TIME, $pLocale); |
| |
| return TRUE; |
| } |
| |
| |
| function GetDayAbb() { |
| return $this->iDayAbb[$this->iLocale]; |
| } |
| |
| function GetShortDay() { |
| return $this->iShortDay[$this->iLocale]; |
| } |
| |
| function GetShortMonth() { |
| return $this->iShortMonth[$this->iLocale]; |
| } |
| |
| function GetShortMonthName($aNbr) { |
| return $this->iShortMonth[$this->iLocale][$aNbr]; |
| } |
| |
| function GetLongMonthName($aNbr) { |
| return $this->iMonthName[$this->iLocale][$aNbr]; |
| } |
| |
| function GetMonth() { |
| return $this->iMonthName[$this->iLocale]; |
| } |
| } |
| |
| $gDateLocale = new DateLocale(); |
| $gJpgDateLocale = new DateLocale(); |
| |
| |
| //======================================================= |
| // CLASS Footer |
| // Description: Encapsulates the footer line in the Graph |
| //======================================================= |
| class Footer { |
| var $left,$center,$right; |
| var $iLeftMargin = 3; |
| var $iRightMargin = 3; |
| var $iBottomMargin = 3; |
| |
| function Footer() { |
| $this->left = new Text(); |
| $this->left->ParagraphAlign('left'); |
| $this->center = new Text(); |
| $this->center->ParagraphAlign('center'); |
| $this->right = new Text(); |
| $this->right->ParagraphAlign('right'); |
| } |
| |
| function Stroke(&$aImg) { |
| $y = $aImg->height - $this->iBottomMargin; |
| $x = $this->iLeftMargin; |
| $this->left->Align('left','bottom'); |
| $this->left->Stroke($aImg,$x,$y); |
| |
| $x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2; |
| $this->center->Align('center','bottom'); |
| $this->center->Stroke($aImg,$x,$y); |
| |
| $x = $aImg->width - $this->iRightMargin; |
| $this->right->Align('right','bottom'); |
| $this->right->Stroke($aImg,$x,$y); |
| } |
| } |
| |
| |
| //=================================================== |
| // CLASS Graph |
| // Description: Main class to handle graphs |
| //=================================================== |
| class Graph { |
| var $cache=null; // Cache object (singleton) |
| var $img=null; // Img object (singleton) |
| var $plots=array(); // Array of all plot object in the graph (for Y 1 axis) |
| var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis) |
| var $ynplots=array(); |
| var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale |
| var $yscale=null,$y2scale=null, $ynscale=array(); |
| var $iIcons = array(); // Array of Icons to add to |
| var $cache_name; // File name to be used for the current graph in the cache directory |
| var $xgrid=null; // X Grid object (linear or logarithmic) |
| var $ygrid=null,$y2grid=null; |
| var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph |
| var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area |
| var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph |
| var $xaxis=null; // X-axis (instane of Axis class) |
| var $yaxis=null, $y2axis=null, $ynaxis=array(); // Y axis (instance of Axis class) |
| var $margin_color=array(200,200,200); // Margin color of graph |
| var $plotarea_color=array(255,255,255); // Plot area color |
| var $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object |
| var $axtype="linlin"; // Type of axis |
| var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with |
| var $texts=null, $y2texts=null; // Text object to ge shown in the graph |
| var $lines=null, $y2lines=null; |
| var $bands=null, $y2bands=null; |
| var $text_scale_off=0, $text_scale_abscenteroff=-1; // Text scale offset in fractions and for centering bars in absolute pixels |
| var $background_image="",$background_image_type=-1,$background_image_format="png"; |
| var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0; |
| var $image_bright=0, $image_contr=0, $image_sat=0; |
| var $inline; |
| var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0 |
| var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default |
| var $iAxisStyle = AXSTYLE_SIMPLE; |
| var $iCSIMdisplay=false,$iHasStroked = false; |
| var $footer; |
| var $csimcachename = '', $csimcachetimeout = 0; |
| var $iDoClipping = false; |
| var $y2orderback=true; |
| var $tabtitle; |
| var $bkg_gradtype=-1,$bkg_gradstyle=BGRAD_MARGIN; |
| var $bkg_gradfrom='navy', $bkg_gradto='silver'; |
| var $titlebackground = false; |
| var $titlebackground_color = 'lightblue', |
| $titlebackground_style = 1, |
| $titlebackground_framecolor = 'blue', |
| $titlebackground_framestyle = 2, |
| $titlebackground_frameweight = 1, |
| $titlebackground_bevelheight = 3 ; |
| var $titlebkg_fillstyle=TITLEBKG_FILLSTYLE_SOLID; |
| var $titlebkg_scolor1='black',$titlebkg_scolor2='white'; |
| var $framebevel = false, $framebeveldepth = 2 ; |
| var $framebevelborder = false, $framebevelbordercolor='black'; |
| var $framebevelcolor1='white@0.4', $framebevelcolor2='black@0.4'; |
| var $background_image_mix=100; |
| var $background_cflag = ''; |
| var $background_cflag_type = BGIMG_FILLPLOT; |
| var $background_cflag_mix = 100; |
| var $iImgTrans=false, |
| $iImgTransHorizon = 100,$iImgTransSkewDist=150, |
| $iImgTransDirection = 1, $iImgTransMinSize = true, |
| $iImgTransFillColor='white',$iImgTransHighQ=false, |
| $iImgTransBorder=false,$iImgTransHorizonPos=0.5; |
| var $iYAxisDeltaPos=50; |
| var $iIconDepth=DEPTH_BACK; |
| var $iAxisLblBgType = 0, |
| $iXAxisLblBgFillColor = 'lightgray', $iXAxisLblBgColor = 'black', |
| $iYAxisLblBgFillColor = 'lightgray', $iYAxisLblBgColor = 'black'; |
| var $iTables=NULL; |
| |
| //--------------- |
| // CONSTRUCTOR |
| |
| // aWIdth Width in pixels of image |
| // aHeight Height in pixels of image |
| // aCachedName Name for image file in cache directory |
| // aTimeOut Timeout in minutes for image in cache |
| // aInline If true the image is streamed back in the call to Stroke() |
| // If false the image is just created in the cache |
| function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) { |
| GLOBAL $gJpgBrandTiming; |
| // If timing is used create a new timing object |
| if( $gJpgBrandTiming ) { |
| global $tim; |
| $tim = new JpgTimer(); |
| $tim->Push(); |
| } |
| |
| if( !is_numeric($aWidth) || !is_numeric($aHeight) ) { |
| JpGraphError::RaiseL(25008);//('Image width/height argument in Graph::Graph() must be numeric'); |
| } |
| |
| // Automatically generate the image file name based on the name of the script that |
| // generates the graph |
| if( $aCachedName=="auto" ) |
| $aCachedName=GenImgName(); |
| |
| // Should the image be streamed back to the browser or only to the cache? |
| $this->inline=$aInline; |
| |
| $this->img = new RotImage($aWidth,$aHeight); |
| |
| $this->cache = new ImgStreamCache($this->img); |
| $this->cache->SetTimeOut($aTimeOut); |
| |
| $this->title = new Text(); |
| $this->title->ParagraphAlign('center'); |
| $this->title->SetFont(FF_FONT2,FS_BOLD); |
| $this->title->SetMargin(3); |
| $this->title->SetAlign('center'); |
| |
| $this->subtitle = new Text(); |
| $this->subtitle->ParagraphAlign('center'); |
| $this->subtitle->SetMargin(2); |
| $this->subtitle->SetAlign('center'); |
| |
| $this->subsubtitle = new Text(); |
| $this->subsubtitle->ParagraphAlign('center'); |
| $this->subsubtitle->SetMargin(2); |
| $this->subsubtitle->SetAlign('center'); |
| |
| $this->legend = new Legend(); |
| $this->footer = new Footer(); |
| |
| // Window doesn't like '?' in the file name so replace it with an '_' |
| $aCachedName = str_replace("?","_",$aCachedName); |
| |
| // If the cached version exist just read it directly from the |
| // cache, stream it back to browser and exit |
| if( $aCachedName!="" && READ_CACHE && $aInline ) |
| if( $this->cache->GetAndStream($aCachedName) ) { |
| exit(); |
| } |
| |
| $this->cache_name = $aCachedName; |
| $this->SetTickDensity(); // Normal density |
| |
| $this->tabtitle = new GraphTabTitle(); |
| } |
| //--------------- |
| // PUBLIC METHODS |
| // Enable final image perspective transformation |
| function Set3DPerspective($aDir=1,$aHorizon=100,$aSkewDist=120,$aQuality=false,$aFillColor='#FFFFFF',$aBorder=false,$aMinSize=true,$aHorizonPos=0.5) { |
| $this->iImgTrans = true; |
| $this->iImgTransHorizon = $aHorizon; |
| $this->iImgTransSkewDist= $aSkewDist; |
| $this->iImgTransDirection = $aDir; |
| $this->iImgTransMinSize = $aMinSize; |
| $this->iImgTransFillColor=$aFillColor; |
| $this->iImgTransHighQ=$aQuality; |
| $this->iImgTransBorder=$aBorder; |
| $this->iImgTransHorizonPos=$aHorizonPos; |
| } |
| |
| // Set Image format and optional quality |
| function SetImgFormat($aFormat,$aQuality=75) { |
| $this->img->SetImgFormat($aFormat,$aQuality); |
| } |
| |
| // Should the grid be in front or back of the plot? |
| function SetGridDepth($aDepth) { |
| $this->grid_depth=$aDepth; |
| } |
| |
| function SetIconDepth($aDepth) { |
| $this->iIconDepth=$aDepth; |
| } |
| |
| // Specify graph angle 0-360 degrees. |
| function SetAngle($aAngle) { |
| $this->img->SetAngle($aAngle); |
| } |
| |
| function SetAlphaBlending($aFlg=true) { |
| $this->img->SetAlphaBlending($aFlg); |
| } |
| |
| // Shortcut to image margin |
| function SetMargin($lm,$rm,$tm,$bm) { |
| $this->img->SetMargin($lm,$rm,$tm,$bm); |
| } |
| |
| function SetY2OrderBack($aBack=true) { |
| $this->y2orderback = $aBack; |
| } |
| |
| // Rotate the graph 90 degrees and set the margin |
| // when we have done a 90 degree rotation |
| function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) { |
| $lm = $lm ==0 ? floor(0.2 * $this->img->width) : $lm ; |
| $rm = $rm ==0 ? floor(0.1 * $this->img->width) : $rm ; |
| $tm = $tm ==0 ? floor(0.2 * $this->img->height) : $tm ; |
| $bm = $bm ==0 ? floor(0.1 * $this->img->height) : $bm ; |
| |
| $adj = ($this->img->height - $this->img->width)/2; |
| $this->img->SetMargin($tm-$adj,$bm-$adj,$rm+$adj,$lm+$adj); |
| $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2)); |
| $this->SetAngle(90); |
| if( empty($this->yaxis) || empty($this->xaxis) ) { |
| JpgraphError::RaiseL(25009);//('You must specify what scale to use with a call to Graph::SetScale()'); |
| } |
| $this->xaxis->SetLabelAlign('right','center'); |
| $this->yaxis->SetLabelAlign('center','bottom'); |
| } |
| |
| function SetClipping($aFlg=true) { |
| $this->iDoClipping = $aFlg ; |
| } |
| |
| // Add a plot object to the graph |
| function Add(&$aPlot) { |
| if( $aPlot == null ) |
| JpGraphError::RaiseL(25010);//("Graph::Add() You tried to add a null plot to the graph."); |
| if( is_array($aPlot) && count($aPlot) > 0 ) |
| $cl = $aPlot[0]; |
| else |
| $cl = $aPlot; |
| |
| if( is_a($cl,'Text') ) |
| $this->AddText($aPlot); |
| elseif( is_a($cl,'PlotLine') ) |
| $this->AddLine($aPlot); |
| elseif( is_a($cl,'PlotBand') ) |
| $this->AddBand($aPlot); |
| elseif( is_a($cl,'IconPlot') ) |
| $this->AddIcon($aPlot); |
| elseif( is_a($cl,'GTextTable') ) |
| $this->AddTable($aPlot); |
| else |
| $this->plots[] = &$aPlot; |
| } |
| |
| |
| function AddTable(&$aTable) { |
| if( is_array($aTable) ) { |
| for($i=0; $i < count($aTable); ++$i ) |
| $this->iTables[]=&$aTable[$i]; |
| } |
| else { |
| $this->iTables[] = &$aTable ; |
| } |
| } |
| |
| function AddIcon(&$aIcon) { |
| if( is_array($aIcon) ) { |
| for($i=0; $i < count($aIcon); ++$i ) |
| $this->iIcons[]=&$aIcon[$i]; |
| } |
| else { |
| $this->iIcons[] = &$aIcon ; |
| } |
| } |
| |
| // Add plot to second Y-scale |
| function AddY2(&$aPlot) { |
| if( $aPlot == null ) |
| JpGraphError::RaiseL(25011);//("Graph::AddY2() You tried to add a null plot to the graph."); |
| |
| if( is_array($aPlot) && count($aPlot) > 0 ) |
| $cl = $aPlot[0]; |
| else |
| $cl = $aPlot; |
| |
| if( is_a($cl,'Text') ) |
| $this->AddText($aPlot,true); |
| elseif( is_a($cl,'PlotLine') ) |
| $this->AddLine($aPlot,true); |
| elseif( is_a($cl,'PlotBand') ) |
| $this->AddBand($aPlot,true); |
| else |
| $this->y2plots[] = &$aPlot; |
| } |
| |
| // Add plot to second Y-scale |
| function AddY($aN,&$aPlot) { |
| |
| if( $aPlot == null ) |
| JpGraphError::RaiseL(25012);//("Graph::AddYN() You tried to add a null plot to the graph."); |
| |
| if( is_array($aPlot) && count($aPlot) > 0 ) |
| $cl = $aPlot[0]; |
| else |
| $cl = $aPlot; |
| |
| if( is_a($cl,'Text') || is_a($cl,'PlotLine') || is_a($cl,'PlotBand') ) |
| JpGraph::RaiseL(25013);//('You can only add standard plots to multiple Y-axis'); |
| else |
| $this->ynplots[$aN][] = &$aPlot; |
| } |
| |
| // Add text object to the graph |
| function AddText(&$aTxt,$aToY2=false) { |
| if( $aTxt == null ) |
| JpGraphError::RaiseL(25014);//("Graph::AddText() You tried to add a null text to the graph."); |
| if( $aToY2 ) { |
| if( is_array($aTxt) ) { |
| for($i=0; $i < count($aTxt); ++$i ) |
| $this->y2texts[]=&$aTxt[$i]; |
| } |
| else |
| $this->y2texts[] = &$aTxt; |
| } |
| else { |
| if( is_array($aTxt) ) { |
| for($i=0; $i < count($aTxt); ++$i ) |
| $this->texts[]=&$aTxt[$i]; |
| } |
| else |
| $this->texts[] = &$aTxt; |
| } |
| } |
| |
| // Add a line object (class PlotLine) to the graph |
| function AddLine(&$aLine,$aToY2=false) { |
| if( $aLine == null ) |
| JpGraphError::RaiseL(25015);//("Graph::AddLine() You tried to add a null line to the graph."); |
| |
| if( $aToY2 ) { |
| if( is_array($aLine) ) { |
| for($i=0; $i < count($aLine); ++$i ) |
| $this->y2lines[]=&$aLine[$i]; |
| } |
| else |
| $this->y2lines[] = &$aLine; |
| } |
| else { |
| if( is_array($aLine) ) { |
| for($i=0; $i < count($aLine); ++$i ) |
| $this->lines[]=&$aLine[$i]; |
| } |
| else |
| $this->lines[] = &$aLine; |
| } |
| } |
| |
| // Add vertical or horizontal band |
| function AddBand(&$aBand,$aToY2=false) { |
| if( $aBand == null ) |
| JpGraphError::RaiseL(25016);//(" Graph::AddBand() You tried to add a null band to the graph."); |
| |
| if( $aToY2 ) { |
| if( is_array($aBand) ) { |
| for($i=0; $i < count($aBand); ++$i ) |
| $this->y2bands[] = &$aBand[$i]; |
| } |
| else |
| $this->y2bands[] = &$aBand; |
| } |
| else { |
| if( is_array($aBand) ) { |
| for($i=0; $i < count($aBand); ++$i ) |
| $this->bands[] = &$aBand[$i]; |
| } |
| else |
| $this->bands[] = &$aBand; |
| } |
| } |
| |
| function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=2,$aStyle=BGRAD_FRAME) { |
| $this->bkg_gradtype=$aGradType; |
| $this->bkg_gradstyle=$aStyle; |
| $this->bkg_gradfrom = $aFrom; |
| $this->bkg_gradto = $aTo; |
| } |
| |
| // Set a country flag in the background |
| function SetBackgroundCFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) { |
| $this->background_cflag = $aName; |
| $this->background_cflag_type = $aBgType; |
| $this->background_cflag_mix = $aMix; |
| } |
| |
| // Alias for the above method |
| function SetBackgroundCountryFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) { |
| $this->background_cflag = $aName; |
| $this->background_cflag_type = $aBgType; |
| $this->background_cflag_mix = $aMix; |
| } |
| |
| |
| // Specify a background image |
| function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") { |
| |
| if( $GLOBALS['gd2'] && !USE_TRUECOLOR ) { |
| JpGraphError::RaiseL(25017);//("You are using GD 2.x and are trying to use a background images on a non truecolor image. To use background images with GD 2.x you <b>must</b> enable truecolor by setting the USE_TRUECOLOR constant to TRUE. Due to a bug in GD 2.0.1 using any truetype fonts with truecolor images will result in very poor quality fonts."); |
| } |
| |
| // Get extension to determine image type |
| if( $aImgFormat == "auto" ) { |
| $e = explode('.',$aFileName); |
| if( !$e ) { |
| JpGraphError::RaiseL(25018,$aFileName);//('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type'); |
| } |
| |
| $valid_formats = array('png', 'jpg', 'gif'); |
| $aImgFormat = strtolower($e[count($e)-1]); |
| if ($aImgFormat == 'jpeg') { |
| $aImgFormat = 'jpg'; |
| } |
| elseif (!in_array($aImgFormat, $valid_formats) ) { |
| JpGraphError::RaiseL(25019,$aImgFormat);//('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName); |
| } |
| } |
| |
| $this->background_image = $aFileName; |
| $this->background_image_type=$aBgType; |
| $this->background_image_format=$aImgFormat; |
| } |
| |
| function SetBackgroundImageMix($aMix) { |
| $this->background_image_mix = $aMix ; |
| } |
| |
| // Adjust brightness and constrast for background image |
| function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) { |
| $this->background_image_bright=$aBright; |
| $this->background_image_contr=$aContr; |
| $this->background_image_sat=$aSat; |
| } |
| |
| // Adjust brightness and constrast for image |
| function AdjImage($aBright,$aContr=0,$aSat=0) { |
| $this->image_bright=$aBright; |
| $this->image_contr=$aContr; |
| $this->image_sat=$aSat; |
| } |
| |
| // Specify axis style (boxed or single) |
| function SetAxisStyle($aStyle) { |
| $this->iAxisStyle = $aStyle ; |
| } |
| |
| // Set a frame around the plot area |
| function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) { |
| $this->boxed = $aDrawPlotFrame; |
| $this->box_weight = $aPlotFrameWeight; |
| $this->box_color = $aPlotFrameColor; |
| } |
| |
| // Specify color for the plotarea (not the margins) |
| function SetColor($aColor) { |
| $this->plotarea_color=$aColor; |
| } |
| |
| // Specify color for the margins (all areas outside the plotarea) |
| function SetMarginColor($aColor) { |
| $this->margin_color=$aColor; |
| } |
| |
| // Set a frame around the entire image |
| function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) { |
| $this->doframe = $aDrawImgFrame; |
| $this->frame_color = $aImgFrameColor; |
| $this->frame_weight = $aImgFrameWeight; |
| } |
| |
| function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor='black',$aColor1='white@0.4',$aColor2='darkgray@0.4',$aFlg=true) { |
| $this->framebevel = $aFlg ; |
| $this->framebeveldepth = $aDepth ; |
| $this->framebevelborder = $aBorder ; |
| $this->framebevelbordercolor = $aBorderColor ; |
| $this->framebevelcolor1 = $aColor1 ; |
| $this->framebevelcolor2 = $aColor2 ; |
| |
| $this->doshadow = false ; |
| } |
| |
| // Set the shadow around the whole image |
| function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) { |
| $this->doshadow = $aShowShadow; |
| $this->shadow_color = $aShadowColor; |
| $this->shadow_width = $aShadowWidth; |
| $this->footer->iBottomMargin += $aShadowWidth; |
| $this->footer->iRightMargin += $aShadowWidth; |
| } |
| |
| // Specify x,y scale. Note that if you manually specify the scale |
| // you must also specify the tick distance with a call to Ticks::Set() |
| function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) { |
| $this->axtype = $aAxisType; |
| |
| if( $aYMax < $aYMin || $aXMax < $aXMin ) |
| JpGraphError::RaiseL(25020);//('Graph::SetScale(): Specified Max value must be larger than the specified Min value.'); |
| |
| $yt=substr($aAxisType,-3,3); |
| if( $yt=="lin" ) |
| $this->yscale = new LinearScale($aYMin,$aYMax); |
| elseif( $yt == "int" ) { |
| $this->yscale = new LinearScale($aYMin,$aYMax); |
| $this->yscale->SetIntScale(); |
| } |
| elseif( $yt=="log" ) |
| $this->yscale = new LogScale($aYMin,$aYMax); |
| else |
| JpGraphError::RaiseL(25021,$aAxisType);//("Unknown scale specification for Y-scale. ($aAxisType)"); |
| |
| $xt=substr($aAxisType,0,3); |
| if( $xt == "lin" || $xt == "tex" ) { |
| $this->xscale = new LinearScale($aXMin,$aXMax,"x"); |
| $this->xscale->textscale = ($xt == "tex"); |
| } |
| elseif( $xt == "int" ) { |
| $this->xscale = new LinearScale($aXMin,$aXMax,"x"); |
| $this->xscale->SetIntScale(); |
| } |
| elseif( $xt == "dat" ) { |
| $this->xscale = new DateScale($aXMin,$aXMax,"x"); |
| } |
| elseif( $xt == "log" ) |
| $this->xscale = new LogScale($aXMin,$aXMax,"x"); |
| else |
| JpGraphError::RaiseL(25022,$aAxisType);//(" Unknown scale specification for X-scale. ($aAxisType)"); |
| |
| $this->xaxis = new Axis($this->img,$this->xscale); |
| $this->yaxis = new Axis($this->img,$this->yscale); |
| $this->xgrid = new Grid($this->xaxis); |
| $this->ygrid = new Grid($this->yaxis); |
| $this->ygrid->Show(); |
| } |
| |
| // Specify secondary Y scale |
| function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) { |
| if( $aAxisType=="lin" ) |
| $this->y2scale = new LinearScale($aY2Min,$aY2Max); |
| elseif( $aAxisType == "int" ) { |
| $this->y2scale = new LinearScale($aY2Min,$aY2Max); |
| $this->y2scale->SetIntScale(); |
| } |
| elseif( $aAxisType=="log" ) { |
| $this->y2scale = new LogScale($aY2Min,$aY2Max); |
| } |
| else JpGraphError::RaiseL(25023,$aAxisType);//("JpGraph: Unsupported Y2 axis type: $aAxisType\nMust be one of (lin,log,int)"); |
| |
| $this->y2axis = new Axis($this->img,$this->y2scale); |
| $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT); |
| $this->y2axis->SetLabelSide(SIDE_RIGHT); |
| $this->y2axis->SetPos('max'); |
| $this->y2axis->SetTitleSide(SIDE_RIGHT); |
| |
| // Deafult position is the max x-value |
| $this->y2grid = new Grid($this->y2axis); |
| } |
| |
| // Set the delta position (in pixels) between the multiple Y-axis |
| function SetYDeltaDist($aDist) { |
| $this->iYAxisDeltaPos = $aDist; |
| } |
| |
| // Specify secondary Y scale |
| function SetYScale($aN,$aAxisType="lin",$aYMin=1,$aYMax=1) { |
| |
| if( $aAxisType=="lin" ) |
| $this->ynscale[$aN] = new LinearScale($aYMin,$aYMax); |
| elseif( $aAxisType == "int" ) { |
| $this->ynscale[$aN] = new LinearScale($aYMin,$aYMax); |
| $this->ynscale[$aN]->SetIntScale(); |
| } |
| elseif( $aAxisType=="log" ) { |
| $this->ynscale[$aN] = new LogScale($aYMin,$aYMax); |
| } |
| else JpGraphError::RaiseL(25024,$aAxisType);//("JpGraph: Unsupported Y axis type: $aAxisType\nMust be one of (lin,log,int)"); |
| |
| $this->ynaxis[$aN] = new Axis($this->img,$this->ynscale[$aN]); |
| $this->ynaxis[$aN]->scale->ticks->SetDirection(SIDE_LEFT); |
| $this->ynaxis[$aN]->SetLabelSide(SIDE_RIGHT); |
| } |
| |
| |
| // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse' |
| // The dividing factor have been determined heuristically according to my aesthetic |
| // sense (or lack off) y.m.m.v ! |
| function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) { |
| $this->xtick_factor=30; |
| $this->ytick_factor=25; |
| switch( $aYDensity ) { |
| case TICKD_DENSE: |
| $this->ytick_factor=12; |
| break; |
| case TICKD_NORMAL: |
| $this->ytick_factor=25; |
| break; |
| case TICKD_SPARSE: |
| $this->ytick_factor=40; |
| break; |
| case TICKD_VERYSPARSE: |
| $this->ytick_factor=100; |
| break; |
| default: |
| JpGraphError::RaiseL(25025,$densy);//("JpGraph: Unsupported Tick density: $densy"); |
| } |
| switch( $aXDensity ) { |
| case TICKD_DENSE: |
| $this->xtick_factor=15; |
| break; |
| case TICKD_NORMAL: |
| $this->xtick_factor=30; |
| break; |
| case TICKD_SPARSE: |
| $this->xtick_factor=45; |
| break; |
| case TICKD_VERYSPARSE: |
| $this->xtick_factor=60; |
| break; |
| default: |
| JpGraphError::RaiseL(25025,$densx);//("JpGraph: Unsupported Tick density: $densx"); |
| } |
| } |
| |
| |
| // Get a string of all image map areas |
| function GetCSIMareas() { |
| if( !$this->iHasStroked ) |
| $this->Stroke(_CSIM_SPECIALFILE); |
| |
| $csim = $this->title->GetCSIMAreas(); |
| $csim .= $this->subtitle->GetCSIMAreas(); |
| $csim .= $this->subsubtitle->GetCSIMAreas(); |
| $csim .= $this->legend->GetCSIMAreas(); |
| |
| if( $this->y2axis != NULL ) { |
| $csim .= $this->y2axis->title->GetCSIMAreas(); |
| } |
| |
| if( $this->texts != null ) { |
| $n = count($this->texts); |
| for($i=0; $i < $n; ++$i ) { |
| $csim .= $this->texts[$i]->GetCSIMAreas(); |
| } |
| } |
| |
| if( $this->y2texts != null && $this->y2scale != null ) { |
| $n = count($this->y2texts); |
| for($i=0; $i < $n; ++$i ) { |
| $csim .= $this->y2texts[$i]->GetCSIMAreas(); |
| } |
| } |
| |
| if( $this->yaxis != null && $this->xaxis != null ) { |
| $csim .= $this->yaxis->title->GetCSIMAreas(); |
| $csim .= $this->xaxis->title->GetCSIMAreas(); |
| } |
| |
| $n = count($this->plots); |
| for( $i=0; $i < $n; ++$i ) |
| $csim .= $this->plots[$i]->GetCSIMareas(); |
| |
| $n = count($this->y2plots); |
| for( $i=0; $i < $n; ++$i ) |
| $csim .= $this->y2plots[$i]->GetCSIMareas(); |
| |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| $m = count($this->ynplots[$i]); |
| for($j=0; $j < $m; ++$j ) { |
| $csim .= $this->ynplots[$i][$j]->GetCSIMareas(); |
| } |
| } |
| |
| $n = count($this->iTables); |
| for( $i=0; $i < $n; ++$i ) { |
| $csim .= $this->iTables[$i]->GetCSIMareas(); |
| } |
| |
| return $csim; |
| } |
| |
| // Get a complete <MAP>..</MAP> tag for the final image map |
| function GetHTMLImageMap($aMapName) { |
| $im = "<map name=\"$aMapName\" id=\"$aMapName\" >\n"; |
| $im .= $this->GetCSIMareas(); |
| $im .= "</map>"; |
| return $im; |
| } |
| |
| function CheckCSIMCache($aCacheName,$aTimeOut=60) { |
| global $_SERVER; |
| |
| if( $aCacheName=='auto' ) |
| $aCacheName=basename($_SERVER['PHP_SELF']); |
| |
| $urlarg = $this->GetURLArguments(); |
| $this->csimcachename = CSIMCACHE_DIR.$aCacheName.$urlarg; |
| $this->csimcachetimeout = $aTimeOut; |
| |
| // First determine if we need to check for a cached version |
| // This differs from the standard cache in the sense that the |
| // image and CSIM map HTML file is written relative to the directory |
| // the script executes in and not the specified cache directory. |
| // The reason for this is that the cache directory is not necessarily |
| // accessible from the HTTP server. |
| if( $this->csimcachename != '' ) { |
| $dir = dirname($this->csimcachename); |
| $base = basename($this->csimcachename); |
| $base = strtok($base,'.'); |
| $suffix = strtok('.'); |
| $basecsim = $dir.'/'.$base.'?'.$urlarg.'_csim_.html'; |
| $baseimg = $dir.'/'.$base.'?'.$urlarg.'.'.$this->img->img_format; |
| |
| $timedout=false; |
| // Does it exist at all ? |
| |
| if( file_exists($basecsim) && file_exists($baseimg) ) { |
| // Check that it hasn't timed out |
| $diff=time()-filemtime($basecsim); |
| if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) { |
| $timedout=true; |
| @unlink($basecsim); |
| @unlink($baseimg); |
| } |
| else { |
| if ($fh = @fopen($basecsim, "r")) { |
| fpassthru($fh); |
| return true; |
| } |
| else |
| JpGraphError::RaiseL(25027,$basecsim);//(" Can't open cached CSIM \"$basecsim\" for reading."); |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Build the argument string to be used with the csim images |
| function GetURLArguments() { |
| |
| // This is a JPGRAPH internal defined that prevents |
| // us from recursively coming here again |
| $urlarg = _CSIM_DISPLAY.'=1'; |
| |
| // Now reconstruct any user URL argument |
| reset($_GET); |
| while( list($key,$value) = each($_GET) ) { |
| if( is_array($value) ) { |
| $n = count($value); |
| for( $i=0; $i < $n; ++$i ) { |
| $urlarg .= '&'.$key.'%5B%5D='.urlencode($value[$i]); |
| } |
| } |
| else { |
| $urlarg .= '&'.$key.'='.urlencode($value); |
| } |
| } |
| |
| // It's not ideal to convert POST argument to GET arguments |
| // but there is little else we can do. One idea for the |
| // future might be recreate the POST header in case. |
| reset($_POST); |
| while( list($key,$value) = each($_POST) ) { |
| if( is_array($value) ) { |
| $n = count($value); |
| for( $i=0; $i < $n; ++$i ) { |
| $urlarg .= '&'.$key.'%5B%5D='.urlencode($value[$i]); |
| } |
| } |
| else { |
| $urlarg .= '&'.$key.'='.urlencode($value); |
| } |
| } |
| |
| return $urlarg; |
| } |
| |
| function StrokeCSIM($aScriptName='auto',$aCSIMName='',$aBorder=0) { |
| if( $aCSIMName=='' ) { |
| // create a random map name |
| srand ((double) microtime() * 1000000); |
| $r = rand(0,100000); |
| $aCSIMName='__mapname'.$r.'__'; |
| } |
| |
| if( $aScriptName=='auto' ) |
| $aScriptName=basename($_SERVER['PHP_SELF']); |
| |
| $urlarg = $this->GetURLArguments(); |
| |
| if( empty($_GET[_CSIM_DISPLAY]) ) { |
| // First determine if we need to check for a cached version |
| // This differs from the standard cache in the sense that the |
| // image and CSIM map HTML file is written relative to the directory |
| // the script executes in and not the specified cache directory. |
| // The reason for this is that the cache directory is not necessarily |
| // accessible from the HTTP server. |
| if( $this->csimcachename != '' ) { |
| $dir = dirname($this->csimcachename); |
| $base = basename($this->csimcachename); |
| $base = strtok($base,'.'); |
| $suffix = strtok('.'); |
| $basecsim = $dir.'/'.$base.'?'.$urlarg.'_csim_.html'; |
| $baseimg = $base.'?'.$urlarg.'.'.$this->img->img_format; |
| |
| // Check that apache can write to directory specified |
| |
| if( file_exists($dir) && !is_writeable($dir) ) { |
| JpgraphError::RaiseL(25028,$dir);//('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.'); |
| } |
| |
| // Make sure directory exists |
| $this->cache->MakeDirs($dir); |
| |
| // Write the image file |
| $this->Stroke(CSIMCACHE_DIR.$baseimg); |
| |
| // Construct wrapper HTML and write to file and send it back to browser |
| |
| // In the src URL we must replace the '?' with its encoding to prevent the arguments |
| // to be converted to real arguments. |
| $tmp = str_replace('?','%3f',$baseimg); |
| $htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n". |
| '<img src="'.CSIMCACHE_HTTP_DIR.$tmp.'" ismap="ismap" usemap="#'.$aCSIMName.'" border="'.$aBorder.'" width="'.$this->img->width.'" height="'.$this->img->height."\" alt=\"\" />\n"; |
| |
| if($fh = @fopen($basecsim,'w') ) { |
| fwrite($fh,$htmlwrap); |
| fclose($fh); |
| echo $htmlwrap; |
| } |
| else |
| JpGraphError::RaiseL(25029,$basecsim);//(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions."); |
| } |
| else { |
| |
| if( $aScriptName=='' ) { |
| JpGraphError::RaiseL(25030);//('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().'); |
| } |
| echo $this->GetHTMLImageMap($aCSIMName); |
| echo "<img src=\"".$aScriptName.'?'.$urlarg."\" ismap=\"ismap\" usemap=\"#".$aCSIMName.'" border="'.$aBorder.'" width="'.$this->img->width.'" height="'.$this->img->height."\" alt=\"\" />\n"; |
| } |
| } |
| else { |
| $this->Stroke(); |
| } |
| } |
| |
| function GetTextsYMinMax($aY2=false) { |
| if( $aY2 ) |
| $txts = $this->y2texts; |
| else |
| $txts = $this->texts; |
| $n = count($txts); |
| $min=null; |
| $max=null; |
| for( $i=0; $i < $n; ++$i ) { |
| if( $txts[$i]->iScalePosY !== null && |
| $txts[$i]->iScalePosX !== null ) { |
| if( $min === null ) { |
| $min = $max = $txts[$i]->iScalePosY ; |
| } |
| else { |
| $min = min($min,$txts[$i]->iScalePosY); |
| $max = max($max,$txts[$i]->iScalePosY); |
| } |
| } |
| } |
| if( $min !== null ) { |
| return array($min,$max); |
| } |
| else |
| return null; |
| } |
| |
| function GetTextsXMinMax($aY2=false) { |
| if( $aY2 ) |
| $txts = $this->y2texts; |
| else |
| $txts = $this->texts; |
| $n = count($txts); |
| $min=null; |
| $max=null; |
| for( $i=0; $i < $n; ++$i ) { |
| if( $txts[$i]->iScalePosY !== null && |
| $txts[$i]->iScalePosX !== null ) { |
| if( $min === null ) { |
| $min = $max = $txts[$i]->iScalePosX ; |
| } |
| else { |
| $min = min($min,$txts[$i]->iScalePosX); |
| $max = max($max,$txts[$i]->iScalePosX); |
| } |
| } |
| } |
| if( $min !== null ) { |
| return array($min,$max); |
| } |
| else |
| return null; |
| } |
| |
| function GetXMinMax() { |
| list($min,$ymin) = $this->plots[0]->Min(); |
| list($max,$ymax) = $this->plots[0]->Max(); |
| foreach( $this->plots as $p ) { |
| list($xmin,$ymin) = $p->Min(); |
| list($xmax,$ymax) = $p->Max(); |
| $min = Min($xmin,$min); |
| $max = Max($xmax,$max); |
| } |
| |
| if( $this->y2axis != null ) { |
| foreach( $this->y2plots as $p ) { |
| list($xmin,$ymin) = $p->Min(); |
| list($xmax,$ymax) = $p->Max(); |
| $min = Min($xmin,$min); |
| $max = Max($xmax,$max); |
| } |
| } |
| |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| if( $this->ynaxis[$i] != null) { |
| foreach( $this->ynplots[$i] as $p ) { |
| list($xmin,$ymin) = $p->Min(); |
| list($xmax,$ymax) = $p->Max(); |
| $min = Min($xmin,$min); |
| $max = Max($xmax,$max); |
| } |
| } |
| } |
| |
| return array($min,$max); |
| } |
| |
| function AdjustMarginsForTitles() { |
| $totrequired = |
| ($this->title->t != '' ? |
| $this->title->GetTextHeight($this->img) + $this->title->margin + 5 : 0 ) + |
| ($this->subtitle->t != '' ? |
| $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 : 0 ) + |
| ($this->subsubtitle->t != '' ? |
| $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 : 0 ) ; |
| |
| |
| $btotrequired = 0; |
| if($this->xaxis != null && !$this->xaxis->hide && !$this->xaxis->hide_labels ) { |
| // Minimum bottom margin |
| if( $this->xaxis->title->t != '' ) { |
| if( $this->img->a == 90 ) |
| $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 5 ; |
| else |
| $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 5 ; |
| } |
| else |
| $btotrequired = 0; |
| |
| if( $this->img->a == 90 ) { |
| $this->img->SetFont($this->yaxis->font_family,$this->yaxis->font_style, |
| $this->yaxis->font_size); |
| $lh = $this->img->GetTextHeight('Mg',$this->yaxis->label_angle); |
| } |
| else { |
| $this->img->SetFont($this->xaxis->font_family,$this->xaxis->font_style, |
| $this->xaxis->font_size); |
| $lh = $this->img->GetTextHeight('Mg',$this->xaxis->label_angle); |
| } |
| |
| $btotrequired += $lh + 5; |
| } |
| |
| if( $this->img->a == 90 ) { |
| // DO Nothing. It gets too messy to do this properly for 90 deg... |
| } |
| else{ |
| if( $this->img->top_margin < $totrequired ) { |
| $this->SetMargin($this->img->left_margin,$this->img->right_margin, |
| $totrequired,$this->img->bottom_margin); |
| } |
| if( $this->img->bottom_margin < $btotrequired ) { |
| $this->SetMargin($this->img->left_margin,$this->img->right_margin, |
| $this->img->top_margin,$btotrequired); |
| } |
| } |
| } |
| |
| // Stroke the graph |
| // $aStrokeFileName If != "" the image will be written to this file and NOT |
| // streamed back to the browser |
| function Stroke($aStrokeFileName="") { |
| |
| // Fist make a sanity check that user has specified a scale |
| if( empty($this->yscale) ) { |
| JpGraphError::RaiseL(25031);//('You must specify what scale to use with a call to Graph::SetScale().'); |
| } |
| |
| // Start by adjusting the margin so that potential titles will fit. |
| $this->AdjustMarginsForTitles(); |
| |
| // Setup scale constants |
| if( $this->yscale ) $this->yscale->InitConstants($this->img); |
| if( $this->xscale ) $this->xscale->InitConstants($this->img); |
| if( $this->y2scale ) $this->y2scale->InitConstants($this->img); |
| |
| $n=count($this->ynscale); |
| for($i=0; $i < $n; ++$i) { |
| if( $this->ynscale[$i] ) $this->ynscale[$i]->InitConstants($this->img); |
| } |
| |
| // If the filename is the predefined value = '_csim_special_' |
| // we assume that the call to stroke only needs to do enough |
| // to correctly generate the CSIM maps. |
| // We use this variable to skip things we don't strictly need |
| // to do to generate the image map to improve performance |
| // a best we can. Therefor you will see a lot of tests !$_csim in the |
| // code below. |
| $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE); |
| |
| // We need to know if we have stroked the plot in the |
| // GetCSIMareas. Otherwise the CSIM hasn't been generated |
| // and in the case of GetCSIM called before stroke to generate |
| // CSIM without storing an image to disk GetCSIM must call Stroke. |
| $this->iHasStroked = true; |
| |
| // Do any pre-stroke adjustment that is needed by the different plot types |
| // (i.e bar plots want's to add an offset to the x-labels etc) |
| for($i=0; $i < count($this->plots) ; ++$i ) { |
| $this->plots[$i]->PreStrokeAdjust($this); |
| $this->plots[$i]->DoLegend($this); |
| } |
| |
| // Any plots on the second Y scale? |
| if( $this->y2scale != null ) { |
| for($i=0; $i<count($this->y2plots) ; ++$i ) { |
| $this->y2plots[$i]->PreStrokeAdjust($this); |
| $this->y2plots[$i]->DoLegend($this); |
| } |
| } |
| |
| // Any plots on the extra Y axises? |
| $n = count($this->ynaxis); |
| for($i=0; $i<$n ; ++$i ) { |
| if( $this->ynplots == null || $this->ynplots[$i] == null) { |
| JpGraphError::RaiseL(25032,$i);//("No plots for Y-axis nbr:$i"); |
| } |
| $m = count($this->ynplots[$i]); |
| for($j=0; $j < $m; ++$j ) { |
| $this->ynplots[$i][$j]->PreStrokeAdjust($this); |
| $this->ynplots[$i][$j]->DoLegend($this); |
| } |
| } |
| |
| |
| // Bail out if any of the Y-axis not been specified and |
| // has no plots. (This means it is impossible to do autoscaling and |
| // no other scale was given so we can't possible draw anything). If you use manual |
| // scaling you also have to supply the tick steps as well. |
| if( (!$this->yscale->IsSpecified() && count($this->plots)==0) || |
| ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) { |
| //$e = "n=".count($this->y2plots)."\n"; |
| // $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n"; |
| // $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n"; |
| // $e .= "2. Specified a scale manually but have forgot to specify the tick steps"; |
| JpGraphError::RaiseL(25026); |
| } |
| |
| // Bail out if no plots and no specified X-scale |
| if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) ) |
| JpGraphError::RaiseL(25034);//("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>"); |
| |
| //Check if we should autoscale y-axis |
| if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) { |
| list($min,$max) = $this->GetPlotsYMinMax($this->plots); |
| $lres = $this->GetLinesYMinMax($this->lines); |
| if( is_array($lres) ) { |
| list($linmin,$linmax) = $lres ; |
| $min = min($min,$linmin); |
| $max = max($max,$linmax); |
| } |
| $tres = $this->GetTextsYMinMax(); |
| if( is_array($tres) ) { |
| list($tmin,$tmax) = $tres ; |
| $min = min($min,$tmin); |
| $max = max($max,$tmax); |
| } |
| $this->yscale->AutoScale($this->img,$min,$max, |
| $this->img->plotheight/$this->ytick_factor); |
| } |
| elseif( $this->yscale->IsSpecified() && |
| ( $this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified()) ) { |
| // The tick calculation will use the user suplied min/max values to determine |
| // the ticks. If auto_ticks is false the exact user specifed min and max |
| // values will be used for the scale. |
| // If auto_ticks is true then the scale might be slightly adjusted |
| // so that the min and max values falls on an even major step. |
| $min = $this->yscale->scale[0]; |
| $max = $this->yscale->scale[1]; |
| $this->yscale->AutoScale($this->img,$min,$max, |
| $this->img->plotheight/$this->ytick_factor, |
| $this->yscale->auto_ticks); |
| } |
| |
| if( $this->y2scale != null) { |
| |
| if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) { |
| list($min,$max) = $this->GetPlotsYMinMax($this->y2plots); |
| $lres = $this->GetLinesYMinMax($this->y2lines); |
| if( is_array($lres) ) { |
| list($linmin,$linmax) = $lres ; |
| $min = min($min,$linmin); |
| $max = max($max,$linmax); |
| } |
| $tres = $this->GetTextsYMinMax(true); |
| if( is_array($tres) ) { |
| list($tmin,$tmax) = $tres ; |
| $min = min($min,$tmin); |
| $max = max($max,$tmax); |
| } |
| $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor); |
| } |
| elseif( $this->y2scale->IsSpecified() && |
| ( $this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified()) ) { |
| // The tick calculation will use the user suplied min/max values to determine |
| // the ticks. If auto_ticks is false the exact user specifed min and max |
| // values will be used for the scale. |
| // If auto_ticks is true then the scale might be slightly adjusted |
| // so that the min and max values falls on an even major step. |
| $min = $this->y2scale->scale[0]; |
| $max = $this->y2scale->scale[1]; |
| $this->y2scale->AutoScale($this->img,$min,$max, |
| $this->img->plotheight/$this->ytick_factor, |
| $this->y2scale->auto_ticks); |
| } |
| } |
| |
| // |
| // Autoscale the multiple Y-axis |
| // |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| if( $this->ynscale[$i] != null) { |
| if( !$this->ynscale[$i]->IsSpecified() && count($this->ynplots[$i])>0 ) { |
| list($min,$max) = $this->GetPlotsYMinMax($this->ynplots[$i]); |
| $this->ynscale[$i]->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor); |
| } |
| elseif( $this->ynscale[$i]->IsSpecified() && |
| ( $this->ynscale[$i]->auto_ticks || !$this->ynscale[$i]->ticks->IsSpecified()) ) { |
| // The tick calculation will use the user suplied min/max values to determine |
| // the ticks. If auto_ticks is false the exact user specifed min and max |
| // values will be used for the scale. |
| // If auto_ticks is true then the scale might be slightly adjusted |
| // so that the min and max values falls on an even major step. |
| $min = $this->ynscale[$i]->scale[0]; |
| $max = $this->ynscale[$i]->scale[1]; |
| $this->ynscale[$i]->AutoScale($this->img,$min,$max, |
| $this->img->plotheight/$this->ytick_factor, |
| $this->ynscale[$i]->auto_ticks); |
| } |
| } |
| } |
| |
| |
| //Check if we should autoscale x-axis |
| if( !$this->xscale->IsSpecified() ) { |
| if( substr($this->axtype,0,4) == "text" ) { |
| $max=0; |
| $n = count($this->plots); |
| for($i=0; $i < $n; ++$i ) { |
| $p = $this->plots[$i]; |
| // We need some unfortunate sub class knowledge here in order |
| // to increase number of data points in case it is a line plot |
| // which has the barcenter set. If not it could mean that the |
| // last point of the data is outside the scale since the barcenter |
| // settings means that we will shift the entire plot half a tick step |
| // to the right in oder to align with the center of the bars. |
| if( is_a($p,'BarPlot') || empty($p->barcenter)) { |
| $max=max($max,$p->numpoints-1); |
| } |
| else { |
| $max=max($max,$p->numpoints); |
| } |
| } |
| $min=0; |
| if( $this->y2axis != null ) { |
| foreach( $this->y2plots as $p ) { |
| $max=max($max,$p->numpoints-1); |
| } |
| } |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| if( $this->ynaxis[$i] != null) { |
| foreach( $this->ynplots[$i] as $p ) { |
| $max=max($max,$p->numpoints-1); |
| } |
| } |
| } |
| |
| $this->xscale->Update($this->img,$min,$max); |
| $this->xscale->ticks->Set($this->xaxis->tick_step,1); |
| $this->xscale->ticks->SupressMinorTickMarks(); |
| } |
| else { |
| list($min,$max) = $this->GetXMinMax(); |
| |
| $lres = $this->GetLinesXMinMax($this->lines); |
| if( $lres ) { |
| list($linmin,$linmax) = $lres ; |
| $min = min($min,$linmin); |
| $max = max($max,$linmax); |
| } |
| $lres = $this->GetLinesXMinMax($this->y2lines); |
| if( $lres ) { |
| list($linmin,$linmax) = $lres ; |
| $min = min($min,$linmin); |
| $max = max($max,$linmax); |
| } |
| |
| $tres = $this->GetTextsXMinMax(); |
| if( $tres ) { |
| list($tmin,$tmax) = $tres ; |
| $min = min($min,$tmin); |
| $max = max($max,$tmax); |
| } |
| |
| $tres = $this->GetTextsXMinMax(true); |
| if( $tres ) { |
| list($tmin,$tmax) = $tres ; |
| $min = min($min,$tmin); |
| $max = max($max,$tmax); |
| } |
| |
| $this->xscale->AutoScale($this->img,$min,$max,round($this->img->plotwidth/$this->xtick_factor)); |
| } |
| |
| //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale |
| if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) ) |
| $this->yaxis->SetPos($this->xscale->GetMinVal()); |
| if( $this->y2axis != null ) { |
| if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) ) |
| $this->y2axis->SetPos($this->xscale->GetMaxVal()); |
| $this->y2axis->SetTitleSide(SIDE_RIGHT); |
| } |
| |
| $n = count($this->ynaxis); |
| $nY2adj = $this->y2axis != null ? $this->iYAxisDeltaPos : 0; |
| for( $i=0; $i < $n; ++$i ) { |
| if( $this->ynaxis[$i] != null ) { |
| if( !is_numeric($this->ynaxis[$i]->pos) && !is_string($this->ynaxis[$i]->pos) ) { |
| $this->ynaxis[$i]->SetPos($this->xscale->GetMaxVal()); |
| $this->ynaxis[$i]->SetPosAbsDelta($i*$this->iYAxisDeltaPos + $nY2adj); |
| } |
| $this->ynaxis[$i]->SetTitleSide(SIDE_RIGHT); |
| } |
| } |
| } |
| elseif( $this->xscale->IsSpecified() && |
| ( $this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified()) ) { |
| // The tick calculation will use the user suplied min/max values to determine |
| // the ticks. If auto_ticks is false the exact user specifed min and max |
| // values will be used for the scale. |
| // If auto_ticks is true then the scale might be slightly adjusted |
| // so that the min and max values falls on an even major step. |
| $min = $this->xscale->scale[0]; |
| $max = $this->xscale->scale[1]; |
| |
| |
| $this->xscale->AutoScale($this->img,$min,$max, |
| $this->img->plotwidth/$this->xtick_factor, |
| false); |
| |
| if( $this->y2axis != null ) { |
| if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) ) |
| $this->y2axis->SetPos($this->xscale->GetMaxVal()); |
| $this->y2axis->SetTitleSide(SIDE_RIGHT); |
| } |
| |
| } |
| |
| // If we have a negative values and x-axis position is at 0 |
| // we need to supress the first and possible the last tick since |
| // they will be drawn on top of the y-axis (and possible y2 axis) |
| // The test below might seem strange the reasone being that if |
| // the user hasn't specified a value for position this will not |
| // be set until we do the stroke for the axis so as of now it |
| // is undefined. |
| // For X-text scale we ignore all this since the tick are usually |
| // much further in and not close to the Y-axis. Hence the test |
| // for 'text' |
| |
| if( ($this->yaxis->pos==$this->xscale->GetMinVal() || |
| (is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) && |
| !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 && |
| substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) { |
| |
| //$this->yscale->ticks->SupressZeroLabel(false); |
| $this->xscale->ticks->SupressFirst(); |
| if( $this->y2axis != null ) { |
| $this->xscale->ticks->SupressLast(); |
| } |
| } |
| elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) { |
| $this->xscale->ticks->SupressLast(); |
| } |
| |
| |
| if( !$_csim ) { |
| $this->StrokePlotArea(); |
| if( $this->iIconDepth == DEPTH_BACK ) { |
| $this->StrokeIcons(); |
| } |
| } |
| $this->StrokeAxis(false); |
| |
| // Stroke bands |
| if( $this->bands != null && !$_csim) |
| for($i=0; $i < count($this->bands); ++$i) { |
| // Stroke all bands that asks to be in the background |
| if( $this->bands[$i]->depth == DEPTH_BACK ) |
| $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale); |
| } |
| |
| if( $this->y2bands != null && $this->y2scale != null && !$_csim ) |
| for($i=0; $i < count($this->y2bands); ++$i) { |
| // Stroke all bands that asks to be in the foreground |
| if( $this->y2bands[$i]->depth == DEPTH_BACK ) |
| $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale); |
| } |
| |
| |
| if( $this->grid_depth == DEPTH_BACK && !$_csim) { |
| $this->ygrid->Stroke(); |
| $this->xgrid->Stroke(); |
| } |
| |
| // Stroke Y2-axis |
| if( $this->y2axis != null && !$_csim) { |
| $this->y2axis->Stroke($this->xscale); |
| $this->y2grid->Stroke(); |
| } |
| |
| // Stroke yn-axis |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| $this->ynaxis[$i]->Stroke($this->xscale); |
| } |
| |
| $oldoff=$this->xscale->off; |
| if(substr($this->axtype,0,4)=="text") { |
| if( $this->text_scale_abscenteroff > -1 ) { |
| // For a text scale the scale factor is the number of pixel per step. |
| // Hence we can use the scale factor as a substitute for number of pixels |
| // per major scale step and use that in order to adjust the offset so that |
| // an object of width "abscenteroff" becomes centered. |
| $this->xscale->off += round($this->xscale->scale_factor/2)-round($this->text_scale_abscenteroff/2); |
| } |
| else { |
| $this->xscale->off += |
| ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step); |
| } |
| } |
| |
| if( $this->iDoClipping ) { |
| $oldimage = $this->img->CloneCanvasH(); |
| } |
| |
| if( ! $this->y2orderback ) { |
| // Stroke all plots for Y axis |
| for($i=0; $i < count($this->plots); ++$i) { |
| $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale); |
| $this->plots[$i]->StrokeMargin($this->img); |
| } |
| } |
| |
| // Stroke all plots for Y2 axis |
| if( $this->y2scale != null ) |
| for($i=0; $i< count($this->y2plots); ++$i ) { |
| $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale); |
| } |
| |
| if( $this->y2orderback ) { |
| // Stroke all plots for Y1 axis |
| for($i=0; $i < count($this->plots); ++$i) { |
| $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale); |
| $this->plots[$i]->StrokeMargin($this->img); |
| } |
| } |
| |
| $n = count($this->ynaxis); |
| for( $i=0; $i < $n; ++$i ) { |
| $m = count($this->ynplots[$i]); |
| for( $j=0; $j < $m; ++$j ) { |
| $this->ynplots[$i][$j]->Stroke($this->img,$this->xscale,$this->ynscale[$i]); |
| $this->ynplots[$i][$j]->StrokeMargin($this->img); |
| } |
| } |
| |
| if( $this->iIconDepth == DEPTH_FRONT) { |
| $this->StrokeIcons(); |
| } |
| |
| if( $this->iDoClipping ) { |
| // Clipping only supports graphs at 0 and 90 degrees |
| if( $this->img->a == 0 ) { |
| $this->img->CopyCanvasH($oldimage,$this->img->img, |
| $this->img->left_margin,$this->img->top_margin, |
| $this->img->left_margin,$this->img->top_margin, |
| $this->img->plotwidth+1,$this->img->plotheight); |
| } |
| elseif( $this->img->a == 90 ) { |
| $adj = ($this->img->height - $this->img->width)/2; |
| $this->img->CopyCanvasH($oldimage,$this->img->img, |
| $this->img->bottom_margin-$adj,$this->img->left_margin+$adj, |
| $this->img->bottom_margin-$adj,$this->img->left_margin+$adj, |
| $this->img->plotheight+1,$this->img->plotwidth); |
| } |
| else { |
| JpGraphError::RaiseL(25035,$this->img->a);//('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.'); |
| } |
| $this->img->Destroy(); |
| $this->img->SetCanvasH($oldimage); |
| } |
| |
| $this->xscale->off=$oldoff; |
| |
| if( $this->grid_depth == DEPTH_FRONT && !$_csim ) { |
| $this->ygrid->Stroke(); |
| $this->xgrid->Stroke(); |
| } |
| |
| // Stroke bands |
| if( $this->bands!= null ) |
| for($i=0; $i < count($this->bands); ++$i) { |
| // Stroke all bands that asks to be in the foreground |
| if( $this->bands[$i]->depth == DEPTH_FRONT ) |
| $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale); |
| } |
| |
| if( $this->y2bands!= null && $this->y2scale != null ) |
| for($i=0; $i < count($this->y2bands); ++$i) { |
| // Stroke all bands that asks to be in the foreground |
| if( $this->y2bands[$i]->depth == DEPTH_FRONT ) |
| $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale); |
| } |
| |
| |
| // Stroke any lines added |
| if( $this->lines != null ) { |
| for($i=0; $i < count($this->lines); ++$i) { |
| $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale); |
| } |
| } |
| |
| if( $this->y2lines != null && $this->y2scale != null ) { |
| for($i=0; $i < count($this->y2lines); ++$i) { |
| $this->y2lines[$i]->Stroke($this->img,$this->xscale,$this->y2scale); |
| } |
| } |
| |
| // Finally draw the axis again since some plots may have nagged |
| // the axis in the edges.However we do no stroke the labels again |
| // since any user defined callback would be called twice. It also |
| // enhances performance. |
| |
| if( !$_csim ) { |
| $this->StrokeAxis(); |
| } |
| |
| if( $this->y2scale != null && !$_csim ) |
| $this->y2axis->Stroke($this->xscale,false); |
| |
| if( !$_csim ) { |
| $this->StrokePlotBox(); |
| } |
| |
| // The titles and legends never gets rotated so make sure |
| // that the angle is 0 before stroking them |
| $aa = $this->img->SetAngle(0); |
| $this->StrokeTitles(); |
| $this->footer->Stroke($this->img); |
| $this->legend->Stroke($this->img); |
| $this->img->SetAngle($aa); |
| $this->StrokeTexts(); |
| $this->StrokeTables(); |
| |
| if( !$_csim ) { |
| |
| $this->img->SetAngle($aa); |
| |
| // Draw an outline around the image map |
| if(_JPG_DEBUG) { |
| $this->DisplayClientSideaImageMapAreas(); |
| } |
| |
| // Adjust the appearance of the image |
| $this->AdjustSaturationBrightnessContrast(); |
| |
| // Should we do any final image transformation |
| if( $this->iImgTrans ) { |
| if( !class_exists('ImgTrans') ) { |
| require_once('jpgraph_imgtrans.php'); |
| //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.'); |
| } |
| |
| $tform = new ImgTrans($this->img->img); |
| $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist, |
| $this->iImgTransDirection,$this->iImgTransHighQ, |
| $this->iImgTransMinSize,$this->iImgTransFillColor, |
| $this->iImgTransBorder); |
| } |
| |
| // If the filename is given as the special "__handle" |
| // then the image handler is returned and the image is NOT |
| // streamed back |
| if( $aStrokeFileName == _IMG_HANDLER ) { |
| return $this->img->img; |
| } |
| else { |
| // Finally stream the generated picture |
| $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName); |
| } |
| } |
| } |
| |
| function SetAxisLabelBackground($aType,$aXFColor='lightgray',$aXColor='black',$aYFColor='lightgray',$aYColor='black') { |
| $this->iAxisLblBgType = $aType; |
| $this->iXAxisLblBgFillColor = $aXFColor; |
| $this->iXAxisLblBgColor = $aXColor; |
| $this->iYAxisLblBgFillColor = $aYFColor; |
| $this->iYAxisLblBgColor = $aYColor; |
| } |
| |
| //--------------- |
| // PRIVATE METHODS |
| |
| function StrokeAxisLabelBackground() { |
| // Types |
| // 0 = No background |
| // 1 = Only X-labels, length of axis |
| // 2 = Only Y-labels, length of axis |
| // 3 = As 1 but extends to width of graph |
| // 4 = As 2 but extends to height of graph |
| // 5 = Combination of 3 & 4 |
| // 6 = Combination of 1 & 2 |
| |
| $t = $this->iAxisLblBgType ; |
| if( $t < 1 ) return; |
| // Stroke optional X-axis label background color |
| if( $t == 1 || $t == 3 || $t == 5 || $t == 6 ) { |
| $this->img->PushColor($this->iXAxisLblBgFillColor); |
| if( $t == 1 || $t == 6 ) { |
| $xl = $this->img->left_margin; |
| $yu = $this->img->height - $this->img->bottom_margin + 1; |
| $xr = $this->img->width - $this->img->right_margin ; |
| $yl = $this->img->height-1-$this->frame_weight; |
| } |
| else { // t==3 || t==5 |
| $xl = $this->frame_weight; |
| $yu = $this->img->height - $this->img->bottom_margin + 1; |
| $xr = $this->img->width - 1 - $this->frame_weight; |
| $yl = $this->img->height-1-$this->frame_weight; |
| } |
| |
| $this->img->FilledRectangle($xl,$yu,$xr,$yl); |
| $this->img->PopColor(); |
| |
| // Check if we should add the vertical lines at left and right edge |
| if( $this->iXAxisLblBgColor !== '' ) { |
| $this->img->PushColor($this->iXAxisLblBgColor); |
| if( $t == 1 || $t == 6 ) { |
| $this->img->Line($xl,$yu,$xl,$yl); |
| $this->img->Line($xr,$yu,$xr,$yl); |
| } |
| else { |
| $xl = $this->img->width - $this->img->right_margin ; |
| $this->img->Line($xl,$yu-1,$xr,$yu-1); |
| } |
| $this->img->PopColor(); |
| } |
| } |
| |
| if( $t == 2 || $t == 4 || $t == 5 || $t == 6 ) { |
| $this->img->PushColor($this->iYAxisLblBgFillColor); |
| if( $t == 2 || $t == 6 ) { |
| $xl = $this->frame_weight; |
| $yu = $this->frame_weight+$this->img->top_margin; |
| $xr = $this->img->left_margin - 1; |
| $yl = $this->img->height - $this->img->bottom_margin + 1; |
| } |
| else { |
| $xl = $this->frame_weight; |
| $yu = $this->frame_weight; |
| $xr = $this->img->left_margin - 1; |
| $yl = $this->img->height-1-$this->frame_weight; |
| } |
| |
| $this->img->FilledRectangle($xl,$yu,$xr,$yl); |
| $this->img->PopColor(); |
| |
| // Check if we should add the vertical lines at left and right edge |
| if( $this->iXAxisLblBgColor !== '' ) { |
| $this->img->PushColor($this->iXAxisLblBgColor); |
| if( $t == 2 || $t == 6 ) { |
| $this->img->Line($xl,$yu-1,$xr,$yu-1); |
| $this->img->Line($xl,$yl-1,$xr,$yl-1); |
| } |
| else { |
| $this->img->Line($xr+1,$yu,$xr+1,$this->img->top_margin); |
| } |
| $this->img->PopColor(); |
| } |
| |
| } |
| } |
| |
| function StrokeAxis($aStrokeLabels=true) { |
| |
| if( $aStrokeLabels ) { |
| $this->StrokeAxisLabelBackground(); |
| } |
| |
| // Stroke axis |
| if( $this->iAxisStyle != AXSTYLE_SIMPLE ) { |
| switch( $this->iAxisStyle ) { |
| case AXSTYLE_BOXIN : |
| $toppos = SIDE_DOWN; |
| $bottompos = SIDE_UP; |
| $leftpos = SIDE_RIGHT; |
| $rightpos = SIDE_LEFT; |
| break; |
| case AXSTYLE_BOXOUT : |
| $toppos = SIDE_UP; |
| $bottompos = SIDE_DOWN; |
| $leftpos = SIDE_LEFT; |
| $rightpos = SIDE_RIGHT; |
| break; |
| case AXSTYLE_YBOXIN: |
| $toppos = -100; |
| $bottompos = SIDE_UP; |
| $leftpos = SIDE_RIGHT; |
| $rightpos = SIDE_LEFT; |
| break; |
| case AXSTYLE_YBOXOUT: |
| $toppos = -100; |
| $bottompos = SIDE_DOWN; |
| $leftpos = SIDE_LEFT; |
| $rightpos = SIDE_RIGHT; |
| break; |
| default: |
| JpGRaphError::RaiseL(25036,$this->iAxisStyle); //('Unknown AxisStyle() : '.$this->iAxisStyle); |
| break; |
| } |
| $this->xaxis->SetPos('min'); |
| |
| // By default we hide the first label so it doesn't cross the |
| // Y-axis in case the positon hasn't been set by the user. |
| // However, if we use a box we always want the first value |
| // displayed so we make sure it will be displayed. |
| $this->xscale->ticks->SupressFirst(false); |
| |
| $this->xaxis->SetLabelSide(SIDE_DOWN); |
| $this->xaxis->scale->ticks->SetSide($bottompos); |
| $this->xaxis->Stroke($this->yscale); |
| |
| if( $toppos != -100 ) { |
| // To avoid side effects we work on a new copy |
| $maxis = $this->xaxis; |
| $maxis->SetPos('max'); |
| $maxis->SetLabelSide(SIDE_UP); |
| $maxis->SetLabelMargin(7); |
| $this->xaxis->scale->ticks->SetSide($toppos); |
| $maxis->Stroke($this->yscale); |
| } |
| |
| $this->yaxis->SetPos('min'); |
| $this->yaxis->SetLabelMargin(10); |
| $this->yaxis->SetLabelSide(SIDE_LEFT); |
| $this->yaxis->scale->ticks->SetSide($leftpos); |
| $this->yaxis->Stroke($this->xscale); |
| |
| $myaxis = $this->yaxis; |
| $myaxis->SetPos('max'); |
| $myaxis->SetLabelMargin(10); |
| $myaxis->SetLabelSide(SIDE_RIGHT); |
| $myaxis->title->Set(''); |
| $myaxis->scale->ticks->SetSide($rightpos); |
| $myaxis->Stroke($this->xscale); |
| |
| } |
| else { |
| $this->xaxis->Stroke($this->yscale,$aStrokeLabels); |
| $this->yaxis->Stroke($this->xscale,$aStrokeLabels); |
| } |
| } |
| |
| |
| // Private helper function for backgound image |
| function LoadBkgImage($aImgFormat='',$aFile='',$aImgStr='') { |
| if( $aImgStr != '' ) { |
| return Image::CreateFromString($aImgStr); |
| } |
| if( $aFile == '' ) |
| $aFile = $this->background_image; |
| // Remove case sensitivity and setup appropriate function to create image |
| // Get file extension. This should be the LAST '.' separated part of the filename |
| $e = explode('.',$aFile); |
| $ext = strtolower($e[count($e)-1]); |
| if ($ext == "jpeg") { |
| $ext = "jpg"; |
| } |
| |
| if( trim($ext) == '' ) |
| $ext = 'png'; // Assume PNG if no extension specified |
| |
| if( $aImgFormat == '' ) |
| $imgtag = $ext; |
| else |
| $imgtag = $aImgFormat; |
| |
| $supported = imagetypes(); |
| if( ( $ext == 'jpg' && !($supported & IMG_JPG) ) || |
| ( $ext == 'gif' && !($supported & IMG_GIF) ) || |
| ( $ext == 'png' && !($supported & IMG_PNG) ) ) { |
| JpGraphError::RaiseL(25037,$aFile);//('The image format of your background image ('.$aFile.') is not supported in your system configuration. '); |
| } |
| |
| |
| if( $imgtag == "jpg" || $imgtag == "jpeg") |
| { |
| $f = "imagecreatefromjpeg"; |
| $imgtag = "jpg"; |
| } |
| else |
| { |
| $f = "imagecreatefrom".$imgtag; |
| } |
| |
| // Compare specified image type and file extension |
| if( $imgtag != $ext ) { |
| //$t = "Background image seems to be of different type (has different file extension) than specified imagetype. Specified: '".$aImgFormat."'File: '".$aFile."'"; |
| JpGraphError::RaiseL(25038, $aImgFormat, $aFile); |
| } |
| |
| $img = @$f($aFile); |
| if( !$img ) { |
| JpGraphError::RaiseL(25039,$aFile);//(" Can't read background image: '".$aFile."'"); |
| } |
| return $img; |
| } |
| |
| function StrokeBackgroundGrad() { |
| if( $this->bkg_gradtype < 0 ) |
| return; |
| $grad = new Gradient($this->img); |
| if( $this->bkg_gradstyle == BGRAD_PLOT ) { |
| $xl = $this->img->left_margin; |
| $yt = $this->img->top_margin; |
| $xr = $xl + $this->img->plotwidth+1 ; |
| $yb = $yt + $this->img->plotheight ; |
| $grad->FilledRectangle($xl,$yt,$xr,$yb,$this->bkg_gradfrom,$this->bkg_gradto,$this->bkg_gradtype); |
| } |
| else { |
| $xl = 0; |
| $yt = 0; |
| $xr = $xl + $this->img->width - 1; |
| $yb = $yt + $this->img->height; |
| if( $this->doshadow ) { |
| $xr -= $this->shadow_width; |
| $yb -= $this->shadow_width; |
| } |
| if( $this->doframe ) { |
| $yt += $this->frame_weight; |
| $yb -= $this->frame_weight; |
| $xl += $this->frame_weight; |
| $xr -= $this->frame_weight; |
| } |
| $aa = $this->img->SetAngle(0); |
| $grad->FilledRectangle($xl,$yt,$xr,$yb,$this->bkg_gradfrom,$this->bkg_gradto,$this->bkg_gradtype); |
| $aa = $this->img->SetAngle($aa); |
| } |
| } |
| |
| function StrokeFrameBackground() { |
| if( $this->background_image != "" && $this->background_cflag != "" ) { |
| JpGraphError::RaiseL(25040);//('It is not possible to specify both a background image and a background country flag.'); |
| } |
| if( $this->background_image != "" ) { |
| $bkgimg = $this->LoadBkgImage($this->background_image_format); |
| $this->img->_AdjBrightContrast($bkgimg,$this->background_image_bright, |
| $this->background_image_contr); |
| $this->img->_AdjSat($bkgimg,$this->background_image_sat); |
| } |
| elseif( $this->background_cflag != "" ) { |
| if( ! class_exists('FlagImages') ) { |
| JpGraphError::RaiseL(25041);//('In order to use Country flags as backgrounds you must include the "jpgraph_flags.php" file.'); |
| } |
| $fobj = new FlagImages(FLAGSIZE4); |
| $dummy=''; |
| $bkgimg = $fobj->GetImgByName($this->background_cflag,$dummy); |
| $this->background_image_mix = $this->background_cflag_mix; |
| $this->background_image_type = $this->background_cflag_type; |
| } |
| else { |
| return ; |
| } |
| |
| $bw = ImageSX($bkgimg); |
| $bh = ImageSY($bkgimg); |
| |
| // No matter what the angle is we always stroke the image and frame |
| // assuming it is 0 degree |
| $aa = $this->img->SetAngle(0); |
| |
| switch( $this->background_image_type ) { |
| case BGIMG_FILLPLOT: // Resize to just fill the plotarea |
| $this->FillMarginArea(); |
| $this->StrokeFrame(); |
| $this->FillPlotArea(); |
| $this->img->CopyMerge($bkgimg, |
| $this->img->left_margin,$this->img->top_margin, |
| 0,0,$this->img->plotwidth+1,$this->img->plotheight, |
| $bw,$bh,$this->background_image_mix); |
| break; |
| case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit |
| $hadj=0; $vadj=0; |
| if( $this->doshadow ) { |
| $hadj = $this->shadow_width; |
| $vadj = $this->shadow_width; |
| } |
| $this->FillMarginArea(); |
| $this->FillPlotArea(); |
| $this->img->CopyMerge($bkgimg,0,0,0,0,$this->img->width-$hadj,$this->img->height-$vadj, |
| $bw,$bh,$this->background_image_mix); |
| $this->StrokeFrame(); |
| break; |
| case BGIMG_COPY: // Just copy the image from left corner, no resizing |
| $this->FillMarginArea(); |
| $this->FillPlotArea(); |
| $this->img->CopyMerge($bkgimg,0,0,0,0,$bw,$bh, |
| $bw,$bh,$this->background_image_mix); |
| $this->StrokeFrame(); |
| break; |
| case BGIMG_CENTER: // Center original image in the plot area |
| $this->FillMarginArea(); |
| $this->FillPlotArea(); |
| $centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2); |
| $centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2); |
| $this->img->CopyMerge($bkgimg,$centerx,$centery,0,0,$bw,$bh, |
| $bw,$bh,$this->background_image_mix); |
| $this->StrokeFrame(); |
| break; |
| default: |
| JpGraphError::RaiseL(25042);//(" Unknown background image layout"); |
| } |
| $this->img->SetAngle($aa); |
| } |
| |
| // Private |
| // Draw a frame around the image |
| function StrokeFrame() { |
| if( !$this->doframe ) return; |
| |
| if( $this->background_image_type <= 1 && |
| ($this->bkg_gradtype < 0 || ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_PLOT)) ) { |
| $c = $this->margin_color; |
| } |
| else { |
| $c = false; |
| } |
| |
| if( $this->doshadow ) { |
| $this->img->SetColor($this->frame_color); |
| $this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height, |
| $c,$this->shadow_width,$this->shadow_color); |
| } |
| elseif( $this->framebevel ) { |
| if( $c ) { |
| $this->img->SetColor($this->margin_color); |
| $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1); |
| } |
| $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2, |
| $this->framebeveldepth, |
| $this->framebevelcolor1,$this->framebevelcolor2); |
| if( $this->framebevelborder ) { |
| $this->img->SetColor($this->framebevelbordercolor); |
| $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1); |
| } |
| } |
| else { |
| $this->img->SetLineWeight($this->frame_weight); |
| if( $c ) { |
| $this->img->SetColor($this->margin_color); |
| $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1); |
| } |
| $this->img->SetColor($this->frame_color); |
| $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1); |
| } |
| } |
| |
| function FillMarginArea() { |
| $hadj=0; $vadj=0; |
| if( $this->doshadow ) { |
| $hadj = $this->shadow_width; |
| $vadj = $this->shadow_width; |
| } |
| |
| $this->img->SetColor($this->margin_color); |
| // $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->height-1-$vadj); |
| |
| $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->top_margin); |
|