PageRenderTime 34ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/bin/jpgraph.php

https://github.com/Robervaldo/web-php
PHP | 5369 lines | 4111 code | 498 blank | 760 comment | 518 complexity | 70348d1c1359125edad9b13c8c67f68e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //=======================================================================
  3. // File: JPGRAPH.PHP
  4. // Description: PHP4 Graph Plotting library. Base module.
  5. // Created: 2001-01-08
  6. // Author: Johan Persson (johanp@aditus.nu)
  7. // Ver: $Id$
  8. //
  9. // License: This code is released under QPL 1.0
  10. // Copyright (C) 2001,2002 Johan Persson
  11. //========================================================================
  12. //------------------------------------------------------------------------
  13. // Directories. Must be updated to reflect your installation
  14. //------------------------------------------------------------------------
  15. // The full absolute name of directory to be used as a cache. This directory MUST
  16. // be readable and writable for PHP. Must end with '/'
  17. DEFINE("CACHE_DIR","/tmp/jpgraph_cache/");
  18. // The URL relative name where the cache can be found, i.e
  19. // under what HTTP directory can the cache be found. Normally
  20. // you would probably assign an alias in apache configuration
  21. // for the cache directory.
  22. DEFINE("APACHE_CACHE_DIR","/jpgraph_cache/");
  23. // Directory for TTF fonts. Must end with '/'
  24. DEFINE("TTF_DIR",'/usr/share/fonts/truetype/');
  25. //------------------------------------------------------------------------
  26. // Various JpGraph Settings. The default should be fine for most
  27. // users.
  28. //------------------------------------------------------------------------
  29. // Specify if we should use GD 2.x or GD 1.x
  30. // If you have GD 2.x installed it is recommended that you use it
  31. // since it will give a slightly, slightly better visual apperance
  32. // for arcs.
  33. DEFINE("USE_LIBRARY_GD2",true);
  34. // Should the image be a truecolor image?
  35. // Note 1: Can only be used with GD 2.0.2 and above.
  36. // Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use
  37. // trucolor. Truecolor support is to be considered alpha since GD 2.x
  38. // is still not considered stable (especially on Win32).
  39. // Note 1: MUST be enabled to get background images working with GD2
  40. // Note 2: If enabled then truetype fonts will look very ugly
  41. // => You can't have both background images and truetype fonts in the same
  42. // image until these bugs has been fixed in GD 2.01
  43. DEFINE('USE_TRUECOLOR',true);
  44. // Deafult graphic format set to "auto" which will automtically
  45. // choose the best available format in the order png,gif,jpg
  46. // (The supported format depends on what your PHP installation supports)
  47. DEFINE("DEFAULT_GFORMAT","auto");
  48. // Determine if the error handler should be image based or purely
  49. // text based. Image based makes it easier since the script will
  50. // always return an image even in case of errors.
  51. DEFINE("USE_IMAGE_ERROR_HANDLER",true);
  52. // Should we try to find an image in the cache before generating it?
  53. // Set this define to false to bypass the reading of the cache and always
  54. // regenerate the image. Note that even if reading the cache is
  55. // disabled the cached will still be updated with the newly generated
  56. // image. Set also "USE_CACHE" below.
  57. DEFINE("READ_CACHE",false);
  58. // Should the cache be used at all? By setting this to false no
  59. // files will be generated in the cache directory.
  60. // The difference from READ_CACHE being that setting READ_CACHE to
  61. // false will still create the image in the cache directory
  62. // just not use it. By setting USE_CACHE=false no files will even
  63. // be generated in the cache directory.
  64. DEFINE("USE_CACHE",false);
  65. // If the color palette is full should JpGraph try to allocate
  66. // the closest match? If you plan on using background image or
  67. // gradient fills it might be a good idea to enable this.
  68. // If not you will otherwise get an error saying that the color palette is
  69. // exhausted. The drawback of using approximations is that the colors
  70. // might not be exactly what you specified.
  71. // Note1: This does only apply to paletted images, not truecolor
  72. // images since they don't have the limitations of maximum number
  73. // of colors.
  74. DEFINE("USE_APPROX_COLORS",true);
  75. // Should usage of deprecated functions and parameters give a fatal error?
  76. // (Useful to check if code is future proof.)
  77. DEFINE("ERR_DEPRECATED",true);
  78. // Should the time taken to generate each picture be branded to the lower
  79. // left in corner in each generated image? Useful for performace measurements
  80. // generating graphs
  81. DEFINE("BRAND_TIMING",false);
  82. // What format should be used for the timing string?
  83. DEFINE("BRAND_TIME_FORMAT","Generated in: %01.3fs");
  84. // What group should the cached file belong to
  85. // (Set to "" will give the default group for the "PHP-user")
  86. // Please note that the Apache user must be a member of the
  87. // specified group since otherwise it is impossible for Apache
  88. // to set the specified group.
  89. DEFINE("CACHE_FILE_GROUP","wwwadmin");
  90. // What permissions should the cached file have
  91. // (Set to "" will give the default persmissions for the "PHP-user")
  92. DEFINE("CACHE_FILE_MOD",0664);
  93. // Decide if we should use the bresenham circle algorithm or the
  94. // built in Arc(). Bresenham gives better visual apperance of circles
  95. // but is more CPU intensive and slower then the built in Arc() function
  96. // in GD. Turned off by default for speed
  97. DEFINE("USE_BRESENHAM",false);
  98. // Special unicode language support
  99. DEFINE("LANGUAGE_CYRILLIC",false);
  100. // Enable some extra debug information to be shown.
  101. // (Should only be used if your first name is Johan)
  102. DEFINE("JPG_DEBUG",false);
  103. //------------------------------------------------------------------
  104. // Constants which are used as parameters for the method calls
  105. //------------------------------------------------------------------
  106. // TTF Font families
  107. DEFINE("FF_COURIER",10);
  108. DEFINE("FF_VERDANA",11);
  109. DEFINE("FF_TIMES",12);
  110. DEFINE("FF_HANDWRT",13);
  111. DEFINE("FF_COMIC",14);
  112. DEFINE("FF_ARIAL",15);
  113. DEFINE("FF_BOOK",16);
  114. // TTF Font styles
  115. DEFINE("FS_NORMAL",1);
  116. DEFINE("FS_BOLD",2);
  117. DEFINE("FS_ITALIC",3);
  118. DEFINE("FS_BOLDIT",4);
  119. //Definitions for internal font, new style
  120. DEFINE("FF_FONT0",1);
  121. DEFINE("FF_FONT1",2);
  122. DEFINE("FF_FONT2",4);
  123. //Definitions for internal font, old style
  124. // (Only defined here to be able to generate an error mesage
  125. // when used)
  126. DEFINE("FONT0",99); // Deprecated from 1.2
  127. DEFINE("FONT1",98); // Deprecated from 1.2
  128. DEFINE("FONT1_BOLD",97); // Deprecated from 1.2
  129. DEFINE("FONT2",96); // Deprecated from 1.2
  130. DEFINE("FONT2_BOLD",95); // Deprecated from 1.2
  131. // Tick density
  132. DEFINE("TICKD_DENSE",1);
  133. DEFINE("TICKD_NORMAL",2);
  134. DEFINE("TICKD_SPARSE",3);
  135. DEFINE("TICKD_VERYSPARSE",4);
  136. // Side for ticks and labels.
  137. DEFINE("SIDE_LEFT",-1);
  138. DEFINE("SIDE_RIGHT",1);
  139. DEFINE("SIDE_DOWN",-1);
  140. DEFINE("SIDE_UP",1);
  141. // Legend type stacked vertical or horizontal
  142. DEFINE("LEGEND_VERT",0);
  143. DEFINE("LEGEND_HOR",1);
  144. // Mark types for plot marks
  145. DEFINE("MARK_SQUARE",1);
  146. DEFINE("MARK_UTRIANGLE",2);
  147. DEFINE("MARK_DTRIANGLE",3);
  148. DEFINE("MARK_DIAMOND",4);
  149. DEFINE("MARK_CIRCLE",5);
  150. DEFINE("MARK_FILLEDCIRCLE",6);
  151. DEFINE("MARK_CROSS",7);
  152. DEFINE("MARK_STAR",8);
  153. DEFINE("MARK_X",9);
  154. // Styles for gradient color fill
  155. DEFINE("GRAD_VER",1);
  156. DEFINE("GRAD_HOR",2);
  157. DEFINE("GRAD_MIDHOR",3);
  158. DEFINE("GRAD_MIDVER",4);
  159. DEFINE("GRAD_CENTER",5);
  160. DEFINE("GRAD_WIDE_MIDVER",6);
  161. DEFINE("GRAD_WIDE_MIDHOR",7);
  162. // Inline defines
  163. DEFINE("INLINE_YES",1);
  164. DEFINE("INLINE_NO",0);
  165. // Format for background images
  166. DEFINE("BGIMG_FILLPLOT",1);
  167. DEFINE("BGIMG_FILLFRAME",2);
  168. DEFINE("BGIMG_COPY",3);
  169. DEFINE("BGIMG_CENTER",4);
  170. // Depth of objects
  171. DEFINE("DEPTH_BACK",0);
  172. DEFINE("DEPTH_FRONT",1);
  173. // Direction
  174. DEFINE("VERTICAL",1);
  175. DEFINE("HORIZONTAL",0);
  176. // Constants for types of static bands in plot area
  177. DEFINE("BAND_RDIAG",1); // Right diagonal lines
  178. DEFINE("BAND_LDIAG",2); // Left diagonal lines
  179. DEFINE("BAND_SOLID",3); // Solid one color
  180. DEFINE("BAND_LVERT",4); // Vertical lines
  181. DEFINE("BAND_LHOR",5); // Horizontal lines
  182. DEFINE("BAND_VLINE",4); // Vertical lines
  183. DEFINE("BAND_HLINE",5); // Horizontal lines
  184. DEFINE("BAND_3DPLANE",6); // "3D" Plane
  185. DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
  186. DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
  187. //
  188. // First of all set up a default error handler
  189. //
  190. //=============================================================
  191. // The default trivial text error handler.
  192. //=============================================================
  193. class JpGraphErrObject {
  194. function JpGraphErrObject() {
  195. // Empty. Reserved for future use
  196. }
  197. // If aHalt is true then execution can't continue. Typical used for
  198. // fatal errors
  199. function Raise($aMsg,$aHalt=true) {
  200. $aMsg = "<b>JpGraph Error:</b> ".$aMsg;
  201. if( $aHalt )
  202. die($aMsg);
  203. else
  204. echo $aMsg."<p>";
  205. }
  206. }
  207. //==============================================================
  208. // An image based error handler
  209. //==============================================================
  210. class JpGraphErrObjectImg {
  211. // Split a line by inserting a newline after aWordCnt words
  212. function InsertLineBreaks($aStr,$aWordCnt=11) {
  213. $tok = strtok($aStr," ");
  214. $cnt = 0;
  215. $s = "";
  216. while( $tok ) {
  217. $s .= $tok;
  218. if( $cnt==$aWordCnt-1 ) {
  219. $s .= "\n";
  220. $cnt = 0;
  221. }
  222. else
  223. $s .= " ";
  224. $cnt++;
  225. $tok = strtok(" ");
  226. }
  227. return $s;
  228. }
  229. function Raise($aMsg,$aHalt=true) {
  230. $w=450; $h=110;
  231. $img = new Image($w,$h);
  232. $img->SetColor("darkred");
  233. $img->Rectangle(0,0,$w-1,$h-1);
  234. $img->SetFont(FF_FONT1,FS_BOLD);
  235. $img->StrokeText(10,20,"JpGraph Error:");
  236. $img->SetColor("black");
  237. $img->SetFont(FF_FONT1,FS_NORMAL);
  238. $txt = new Text($this->InsertLineBreaks($aMsg),10,20);
  239. $txt->Align("left","top");
  240. $txt->Stroke($img);
  241. $img->Headers();
  242. $img->Stream();
  243. die();
  244. }
  245. }
  246. //
  247. // A wrapper class that is used to access the specified error object
  248. // (to hide the global error parameter and avoid having a GLOBAL directive
  249. // in all methods.
  250. //
  251. class JpGraphError {
  252. function Install($aErrObject) {
  253. GLOBAL $__jpg_err;
  254. $__jpg_err = $aErrObject;
  255. }
  256. function Raise($aMsg,$aHalt=true){
  257. GLOBAL $__jpg_err;
  258. $tmp = new $__jpg_err;
  259. $tmp->Raise($aMsg,$aHalt);
  260. }
  261. }
  262. //
  263. // ... and install the default error handler
  264. //
  265. if( USE_IMAGE_ERROR_HANDLER ) {
  266. JpGraphError::Install("JpGraphErrObjectImg");
  267. }
  268. else {
  269. JpGraphError::Install("JpGraphErrObject");
  270. }
  271. //
  272. //Check if there were any warnings, perhaps some wrong includes by the
  273. //user
  274. //
  275. if( isset($GLOBALS['php_errormsg']) ) {
  276. JpGraphError::Raise("<b>General PHP error:</b><br>".$GLOBALS['php_errormsg']);
  277. }
  278. //
  279. // Check what version of the GD library is being used
  280. //
  281. if( USE_LIBRARY_GD2 ) {
  282. $gd2 = true;
  283. $copyfunc = "imagecopyresampled";
  284. } elseif(function_exists('imagecopyresized')) {
  285. $copyfunc = "imagecopyresized";
  286. $gd2 = false;
  287. }
  288. else {
  289. JpGraphError::Raise(" Your PHP installation does not
  290. have the required GD library.
  291. Please see the PHP documentation on how to install and enable the GD library.");
  292. }
  293. // Usefull mathematical function
  294. function sign($a) {if( $a>=0) return 1; else return -1;}
  295. // Utility function to generate an image name based on the filename we
  296. // are running from AND assuming we use auto detection of graphic format
  297. // (top level), i.e it is safe to call this function
  298. // from a script that uses JpGraph
  299. function GenImgName() {
  300. global $HTTP_SERVER_VARS, $PHP_SELF;
  301. $supported = imagetypes();
  302. if( $supported & IMG_PNG )
  303. $img_format="png";
  304. elseif( $supported & IMG_GIF )
  305. $img_format="gif";
  306. elseif( $supported & IMG_JPG )
  307. $img_format="jpeg";
  308. if( isset($HTTP_SERVER_VARS['PHP_SELF']) ) $fname=basename($HTTP_SERVER_VARS['PHP_SELF']);
  309. else $fname=basename($PHP_SELF);
  310. # JpGraphError::Raise(" 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.");
  311. // Replace the ".php" extension with the image format extension
  312. return substr($fname,0,strlen($fname)-4).".".$img_format;
  313. }
  314. class LanguageConv {
  315. // Translate iso encoding to unicode
  316. function iso2uni ($isoline){
  317. for ($i=0; $i < strlen($isoline); $i++){
  318. $thischar=substr($isoline,$i,1);
  319. $charcode=ord($thischar);
  320. $uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar;
  321. }
  322. return $uniline;
  323. }
  324. function ToCyrillic($aTxt) {
  325. $koistring = $aTxt;
  326. $isostring = convert_cyr_string($koistring, "k", "i");
  327. $unistring = LanguageConv::iso2uni($isostring);
  328. $this->t = $unistring;
  329. return $aTxt;
  330. }
  331. }
  332. //===================================================
  333. // CLASS JpgTimer
  334. // Description: General timing utility class to handle
  335. // timne measurement of generating graphs. Multiple
  336. // timers can be started by pushing new on a stack.
  337. //===================================================
  338. class JpgTimer {
  339. var $start;
  340. var $idx;
  341. //---------------
  342. // CONSTRUCTOR
  343. function JpgTimer() {
  344. $this->idx=0;
  345. }
  346. //---------------
  347. // PUBLIC METHODS
  348. // Push a new timer start on stack
  349. function Push() {
  350. list($ms,$s)=explode(" ",microtime());
  351. $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
  352. }
  353. // Pop the latest timer start and return the diff with the
  354. // current time
  355. function Pop() {
  356. assert($this->idx>0);
  357. list($ms,$s)=explode(" ",microtime());
  358. $etime=floor($ms*1000) + (1000*$s);
  359. $this->idx--;
  360. return $etime-$this->start[$this->idx];
  361. }
  362. } // Class
  363. //===================================================
  364. // CLASS Graph
  365. // Description: Main class to handle graphs
  366. //===================================================
  367. class Graph {
  368. var $cache=null; // Cache object (singleton)
  369. var $img=null; // Img object (singleton)
  370. var $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
  371. var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis)
  372. var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
  373. var $yscale=null,$y2scale=null;
  374. var $cache_name; // File name to be used for the current graph in the cache directory
  375. var $xgrid=null; // X Grid object (linear or logarithmic)
  376. var $ygrid=null,$y2grid=null; //dito for Y
  377. var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
  378. var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
  379. var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
  380. var $xaxis=null; // X-axis (instane of Axis class)
  381. var $yaxis=null, $y2axis=null; // Y axis (instance of Axis class)
  382. var $margin_color=array(198,198,198); // Margin coor of graph
  383. var $plotarea_color=array(255,255,255); // Plot area color
  384. var $title,$subtitle; // Title and subtitle text object
  385. var $axtype="linlin"; // Type of axis
  386. var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with
  387. var $texts=null; // Text object to ge shown in the graph
  388. var $lines=null;
  389. var $bands=null;
  390. var $text_scale_off=0; // Text scale offset in world coordinates
  391. var $background_image="",$background_image_type=-1,$background_image_format="png";
  392. var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
  393. var $image_bright=0, $image_contr=0, $image_sat=0;
  394. var $inline;
  395. var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0
  396. var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
  397. //---------------
  398. // CONSTRUCTOR
  399. // aWIdth Width in pixels of image
  400. // aHeight Height in pixels of image
  401. // aCachedName Name for image file in cache directory
  402. // aTimeOut Timeout in minutes for image in cache
  403. // aInline If true the image is streamed back in the call to Stroke()
  404. // If false the image is just created in the cache
  405. function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
  406. // If timing is used create a new timing object
  407. if( BRAND_TIMING ) {
  408. global $tim;
  409. $tim = new JpgTimer();
  410. $tim->Push();
  411. }
  412. // Automtically generate the image file name based on the name of the script that
  413. // generates the graph
  414. if( $aCachedName=="auto" )
  415. $aCachedName=GenImgName();
  416. // Should the image be streamed back to the browser or only to the cache?
  417. $this->inline=$aInline;
  418. $this->img = new RotImage($aWidth,$aHeight);
  419. $this->cache = new ImgStreamCache($this->img);
  420. $this->cache->SetTimeOut($aTimeOut);
  421. $this->title = new Text();
  422. $this->subtitle = new Text();
  423. $this->legend = new Legend();
  424. // If the cached version exist just read it directly from the
  425. // cache, stream it back to browser and exit
  426. if( $aCachedName!="" && READ_CACHE && $aInline )
  427. if( $this->cache->GetAndStream($aCachedName) ) {
  428. exit();
  429. }
  430. $this->cache_name = $aCachedName;
  431. $this->SetTickDensity(); // Normal density
  432. }
  433. //---------------
  434. // PUBLIC METHODS
  435. // Should the grid be in front or back of the plot?
  436. function SetGridDepth($aDepth) {
  437. $this->grid_depth=$aDepth;
  438. }
  439. // Specify graph angle 0-360 degrees.
  440. function SetAngle($aAngle) {
  441. $this->img->SetAngle($aAngle);
  442. }
  443. // Add a plot object to the graph
  444. function Add(&$aPlot) {
  445. if( $aPlot == null )
  446. JpGraphError::Raise("<b></b> Graph::Add() You tried to add a null plot to the graph.");
  447. $this->plots[] = &$aPlot;
  448. }
  449. // Add plot to second Y-scale
  450. function AddY2(&$aPlot) {
  451. if( $aPlot == null )
  452. JpGraphError::Raise("<b></b> Graph::AddY2() You tried to add a null plot to the graph.");
  453. $this->y2plots[] = &$aPlot;
  454. }
  455. // Add text object to the graph
  456. function AddText(&$aTxt) {
  457. if( $aTxt == null )
  458. JpGraphError::Raise("<b></b> Graph::AddText() You tried to add a null text to the graph.");
  459. if( is_array($aTxt) ) {
  460. for($i=0; $i<count($aTxt); ++$i )
  461. $this->texts[]=&$aTxt[$i];
  462. }
  463. else
  464. $this->texts[] = &$aTxt;
  465. }
  466. // Add a line object (class PlotLine) to the graph
  467. function AddLine(&$aLine) {
  468. if( $aLine == null )
  469. JpGraphError::Raise("<b></b> Graph::AddLine() You tried to add a null line to the graph.");
  470. if( is_array($aLine) ) {
  471. for($i=0; $i<count($aLine); ++$i )
  472. $this->lines[]=&$aLine[$i];
  473. }
  474. else
  475. $this->lines[] = &$aLine;
  476. }
  477. // Add vertical or horizontal band
  478. function AddBand(&$aBand) {
  479. if( $aBand == null )
  480. JpGraphError::Raise(" Graph::AddBand() You tried to add a null band to the graph.");
  481. if( is_array($aBand) ) {
  482. for($i=0; $i<count($aBand); ++$i )
  483. $this->bands[] = &$aBand[$i];
  484. }
  485. else
  486. $this->bands[] = &$aBand;
  487. }
  488. // Specify a background image
  489. function SetBackgroundImage($aFileName,$aBgType=BKIMG_FILLPLOT,$aImgFormat="png") {
  490. if( $GLOBALS["gd2"] && !USE_TRUECOLOR ) {
  491. JpGraphError::Raise("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.");
  492. }
  493. $this->background_image = $aFileName;
  494. $this->background_image_type=$aBgType;
  495. $this->background_image_format=$aImgFormat;
  496. }
  497. // Adjust brightness and constrast for background image
  498. function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) {
  499. $this->background_image_bright=$aBright;
  500. $this->background_image_contr=$aContr;
  501. $this->background_image_sat=$aSat;
  502. }
  503. // Adjust brightness and constrast for image
  504. function AdjImage($aBright,$aContr=0,$aSat=0) {
  505. $this->image_bright=$aBright;
  506. $this->image_contr=$aContr;
  507. $this->image_sat=$aSat;
  508. }
  509. // Set a frame around the plot area
  510. function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
  511. $this->boxed = $aDrawPlotFrame;
  512. $this->box_weight = $aPlotFrameWeight;
  513. $this->box_color = $aPlotFrameColor;
  514. }
  515. // Specify color for the plotarea (not the margins)
  516. function SetColor($aColor) {
  517. $this->plotarea_color=$aColor;
  518. }
  519. // Specify color for the margins (all areas outside the plotarea)
  520. function SetMarginColor($aColor) {
  521. $this->margin_color=$aColor;
  522. }
  523. // Set a frame around the entire image
  524. function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
  525. $this->doframe = $aDrawImgFrame;
  526. $this->frame_color = $aImgFrameColor;
  527. $this->frame_weight = $aImgFrameWeight;
  528. }
  529. // Set the shadow around the whole image
  530. function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
  531. $this->doshadow = $aShowShadow;
  532. $this->shadow_color = $aShadowColor;
  533. $this->shadow_width = $aShadowWidth;
  534. }
  535. // Specify x,y scale. Note that if you manually specify the scale
  536. // you must also specify the tick distance with a call to Ticks::Set()
  537. function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
  538. $this->axtype = $aAxisType;
  539. $yt=substr($aAxisType,-3,3);
  540. if( $yt=="lin" )
  541. $this->yscale = new LinearScale($aYMin,$aYMax);
  542. elseif( $yt == "int" ) {
  543. $this->yscale = new LinearScale($aYMin,$aYMax);
  544. $this->yscale->SetIntScale();
  545. }
  546. elseif( $yt=="log" )
  547. $this->yscale = new LogScale($aYMin,$aYMax);
  548. else
  549. JpGraphError::Raise(" Unknown scale specification for Y-scale. ($axtype)");
  550. $xt=substr($aAxisType,0,3);
  551. if( $xt == "lin" || $xt == "tex" )
  552. $this->xscale = new LinearScale($aXMin,$aXMax,"x");
  553. elseif( $xt == "int" ) {
  554. $this->xscale = new LinearScale($aXMin,$aXMax,"x");
  555. $this->xscale->SetIntScale();
  556. }
  557. elseif( $xt == "log" )
  558. $this->xscale = new LogScale($aXMin,$aXMax,"x");
  559. else
  560. JpGraphError::Raise(" Unknown scale specification for X-scale. ($aAxisType)");
  561. $this->xscale->Init($this->img);
  562. $this->yscale->Init($this->img);
  563. $this->xaxis = new Axis($this->img,$this->xscale);
  564. $this->yaxis = new Axis($this->img,$this->yscale);
  565. $this->xgrid = new Grid($this->xaxis);
  566. $this->ygrid = new Grid($this->yaxis);
  567. $this->ygrid->Show();
  568. }
  569. // Specify secondary Y scale
  570. function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
  571. if( $aAxisType=="lin" )
  572. $this->y2scale = new LinearScale($aY2Min,$aY2Max);
  573. elseif( $aAxisType=="log" ) {
  574. $this->y2scale = new LogScale($aY2Min,$aY2Max);
  575. }
  576. else JpGraphError::Raise("JpGraph: Unsupported Y2 axis type: $axtype<br>");
  577. $this->y2scale->Init($this->img);
  578. $this->y2axis = new Axis($this->img,$this->y2scale);
  579. $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
  580. $this->y2axis->SetLabelPos(SIDE_RIGHT);
  581. // Deafult position is the max x-value
  582. $this->y2axis->SetPos($this->xscale->GetMaxVal());
  583. $this->y2grid = new Grid($this->y2axis);
  584. }
  585. // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
  586. // The dividing factor have been determined heuristically according to my aesthetic
  587. // sense (or lack off) y.m.m.v !
  588. function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
  589. $this->xtick_factor=30;
  590. $this->ytick_factor=25;
  591. switch( $aYDensity ) {
  592. case TICKD_DENSE:
  593. $this->ytick_factor=12;
  594. break;
  595. case TICKD_NORMAL:
  596. $this->ytick_factor=25;
  597. break;
  598. case TICKD_SPARSE:
  599. $this->ytick_factor=40;
  600. break;
  601. case TICKD_VERYSPARSE:
  602. $this->ytick_factor=100;
  603. break;
  604. default:
  605. JpGraphError::Raise("JpGraph: Unsupported Tick density: $densy");
  606. }
  607. switch( $aXDensity ) {
  608. case TICKD_DENSE:
  609. $this->xtick_factor=18;
  610. break;
  611. case TICKD_NORMAL:
  612. $this->xtick_factor=30;
  613. break;
  614. case TICKD_SPARSE:
  615. $this->xtick_factor=45;
  616. break;
  617. case TICKD_VERYSPARSE:
  618. $this->xtick_factor=60;
  619. break;
  620. default:
  621. JpGraphError::Raise("JpGraph: Unsupported Tick density: $densx");
  622. }
  623. }
  624. // Get a string of all image map areas
  625. function GetCSIMareas() {
  626. $csim="";
  627. foreach ($this->plots as $p) {
  628. $csim.= $p->GetCSIMareas();
  629. }
  630. return $csim;
  631. }
  632. // Get a complete <MAP>..</MAP> tag for the final image map
  633. function GetHTMLImageMap($aMapName) {
  634. $im = "<MAP NAME=\"$aMapName\">\n";
  635. $im .= $this->GetCSIMareas();
  636. $im .= "</MAP>";
  637. return $im;
  638. }
  639. // Stroke the graph
  640. // $aStrokeFileName If != "" the image will be written to this file and NOT
  641. // streamed back to the browser
  642. function Stroke($aStrokeFileName="") {
  643. // Do any pre-stroke adjustment that is needed by the different plot types
  644. // (i.e bar plots want's to add an offset to the x-labels etc)
  645. for($i=0; $i<count($this->plots) ; ++$i ) {
  646. $this->plots[$i]->PreStrokeAdjust($this);
  647. $this->plots[$i]->Legend($this);
  648. }
  649. // Any plots on the second Y scale?
  650. if( $this->y2scale != null ) {
  651. for($i=0; $i<count($this->y2plots) ; ++$i ) {
  652. $this->y2plots[$i]->PreStrokeAdjust($this);
  653. $this->y2plots[$i]->Legend($this);
  654. }
  655. }
  656. // Bail out if any of the Y-axis not been specified and
  657. // has no plots. (This means it is impossible to do autoscaling and
  658. // no other scale was given so we can't possible draw anything). If you use manual
  659. // scaling you also have to supply the tick steps as well.
  660. if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
  661. ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
  662. JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified Y-scale.</strong><br>
  663. You have either:
  664. <br>* Specified an Y axis for autoscaling but have not supplied any plots
  665. <br>* Specified a scale manually but have forgot to specify the tick steps");
  666. }
  667. // Bail out if no plots and no specified X-scale
  668. if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
  669. JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
  670. //Check if we should autoscale y-axis
  671. if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
  672. list($min,$max) = $this->GetPlotsYMinMax($this->plots);
  673. $this->yscale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  674. }
  675. if( $this->y2scale != null)
  676. if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
  677. list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
  678. $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
  679. }
  680. //Check if we should autoscale x-axis
  681. if( !$this->xscale->IsSpecified() ) {
  682. if( substr($this->axtype,0,4) == "text" ) {
  683. $max=0;
  684. foreach( $this->plots as $p ) {
  685. $max=max($max,$p->numpoints-1);
  686. }
  687. $min=0;
  688. if( $this->y2axis != null ) {
  689. foreach( $this->y2plots as $p ) {
  690. $max=max($max,$p->numpoints-1);
  691. }
  692. }
  693. $this->xscale->Update($this->img,$min,$max);
  694. $this->xscale->ticks->Set($this->xaxis->tick_step,1);
  695. $this->xscale->ticks->SupressMinorTickMarks();
  696. }
  697. else {
  698. list($min,$ymin) = $this->plots[0]->Min();
  699. list($max,$ymax) = $this->plots[0]->Max();
  700. foreach( $this->plots as $p ) {
  701. list($xmin,$ymin) = $p->Min();
  702. list($xmax,$ymax) = $p->Max();
  703. $min = Min($xmin,$min);
  704. $max = Max($xmax,$max);
  705. }
  706. if( $this->y2axis != null ) {
  707. foreach( $this->y2plots as $p ) {
  708. list($xmin,$ymin) = $p->Min();
  709. list($xmax,$ymax) = $p->Max();
  710. $min = Min($xmin,$min);
  711. $max = Max($xmax,$max);
  712. }
  713. }
  714. $this->xscale->AutoScale($this->img,$min,$max,$this->img->plotwidth/$this->xtick_factor);
  715. }
  716. //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
  717. $this->yaxis->SetPos($this->xscale->GetMinVal());
  718. if( $this->y2axis != null ) {
  719. $this->y2axis->SetPos($this->xscale->GetMaxVal());
  720. $this->y2axis->SetTitleSide(SIDE_RIGHT);
  721. }
  722. }
  723. // If we have a negative values and x-axis position is at 0
  724. // we need to supress the first and possible the last tick since
  725. // they will be drawn on top of the y-axis (and possible y2 axis)
  726. // The test below might seem strange the reasone being that if
  727. // the user hasn't specified a value for position this will not
  728. // be set until we do the stroke for the axis so as of now it
  729. // is undefined.
  730. if( !$this->xaxis->pos && $this->yscale->GetMinVal() < 0 ) {
  731. $this->yscale->ticks->SupressZeroLabel(false);
  732. $this->xscale->ticks->SupressFirst();
  733. if( $this->y2axis != null ) {
  734. $this->xscale->ticks->SupressLast();
  735. }
  736. }
  737. $this->StrokePlotArea();
  738. // Stroke axis
  739. $this->xaxis->Stroke($this->yscale);
  740. $this->yaxis->Stroke($this->xscale);
  741. // Stroke bands
  742. if( $this->bands != null )
  743. for($i=0; $i<count($this->bands); ++$i) {
  744. // Stroke all bands that asks to be in the background
  745. if( $this->bands[$i]->depth == DEPTH_BACK )
  746. $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  747. }
  748. if( $this->grid_depth == DEPTH_BACK ) {
  749. $this->ygrid->Stroke();
  750. $this->xgrid->Stroke();
  751. }
  752. // Stroke Y2-axis
  753. if( $this->y2axis != null ) {
  754. $this->y2axis->Stroke($this->xscale);
  755. $this->y2grid->Stroke();
  756. }
  757. $oldoff=$this->xscale->off;
  758. if(substr($this->axtype,0,4)=="text") {
  759. $this->xscale->off +=
  760. ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
  761. }
  762. // Stroke all plots for Y1 axis
  763. for($i=0; $i<count($this->plots) ; ++$i ) {
  764. $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  765. $this->plots[$i]->StrokeMargin($this->img);
  766. }
  767. // Stroke all plots for Y2 axis
  768. if( $this->y2scale != null )
  769. for($i=0; $i< count($this->y2plots); ++$i ) {
  770. $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
  771. }
  772. $this->xscale->off=$oldoff;
  773. if( $this->grid_depth == DEPTH_FRONT ) {
  774. $this->ygrid->Stroke();
  775. $this->xgrid->Stroke();
  776. }
  777. // Stroke bands
  778. if( $this->bands!= null )
  779. for($i=0; $i<count($this->bands); ++$i) {
  780. // Stroke all bands that asks to be in the foreground
  781. if( $this->bands[$i]->depth == DEPTH_FRONT )
  782. $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  783. }
  784. // Stroke any lines added
  785. if( $this->lines != null ) {
  786. for($i=0; $i<count($this->lines); ++$i) {
  787. $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
  788. }
  789. }
  790. // Finally draw the axis again since some plots may have nagged
  791. // the axis in the edges.
  792. $this->yaxis->Stroke($this->xscale);
  793. $this->xaxis->Stroke($this->yscale);
  794. if( $this->y2scale != null)
  795. $this->y2axis->Stroke($this->xscale);
  796. $this->StrokePlotBox();
  797. // The titles and legends never gets rotated so make sure
  798. // that the angle is 0 before stroking them
  799. $aa = $this->img->SetAngle(0);
  800. $this->StrokeTitles();
  801. $this->legend->Stroke($this->img);
  802. $this->StrokeTexts();
  803. $this->img->SetAngle($aa);
  804. // Draw an outline around the image map
  805. if(JPG_DEBUG)
  806. $this->DisplayClientSideaImageMapAreas();
  807. // Adjust the appearance of the image
  808. $this->AdjustSaturationBrightnessContrast();
  809. // Finally stream the generated picture
  810. $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName);
  811. }
  812. //---------------
  813. // PRIVATE METHODS
  814. // Private helper function for backgound image
  815. function LoadBkgImage($aImgFormat="png",$aBright=0,$aContr=0) {
  816. $f = "imagecreatefrom".$aImgFormat;
  817. $imgtag = $aImgFormat;
  818. if( $aImgFormat == "jpeg" )
  819. $imgtag = "jpg";
  820. if( !strstr($this->background_image,$imgtag) && strstr($this->background_image,".") )
  821. JpGraphError::Raise(" Background image seems to be of different type (has different file extension)
  822. than specified imagetype. <br>Specified: '".$aImgFormat."'<br>File: '".$this->background_image."'");
  823. $img = $f($this->background_image);
  824. if( !$img ) {
  825. JpGraphError::Raise(" Can't read background image: '".$this->background_image."'");
  826. }
  827. return $img;
  828. }
  829. // Private
  830. // Stroke the plot area with either a solid color or a background image
  831. function StrokePlotArea() {
  832. // Copy in background image
  833. if( $this->background_image != "" ) {
  834. $bkgimg = $this->LoadBkgImage($this->background_image_format);
  835. $this->img->_AdjBrightContrast($bkgimg,$this->background_image_bright,
  836. $this->background_image_contr);
  837. $this->img->_AdjSat($bkgimg,$this->background_image_sat);
  838. $bw = ImageSX($bkgimg);
  839. $bh = ImageSY($bkgimg);
  840. $aa = $this->img->SetAngle(0);
  841. switch( $this->background_image_type ) {
  842. case BGIMG_FILLPLOT: // Resize to just fill the plotarea
  843. $this->StrokeFrame();
  844. $GLOBALS["copyfunc"]($this->img->img,$bkgimg,
  845. $this->img->left_margin,$this->img->top_margin,
  846. 0,0,$this->img->plotwidth,$this->img->plotheight,
  847. $bw,$bh);
  848. break;
  849. case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
  850. $GLOBALS["copyfunc"]($this->img->img,$bkgimg,
  851. 0,0,0,0,
  852. $this->img->width,$this->img->height,
  853. $bw,$bh);
  854. $this->StrokeFrame();
  855. break;
  856. case BGIMG_COPY: // Just copy the image from left corner, no resizing
  857. $GLOBALS["copyfunc"]($this->img->img,$bkgimg,
  858. 0,0,0,0,
  859. $bw,$bh,
  860. $bw,$bh);
  861. $this->StrokeFrame();
  862. break;
  863. case BGIMG_CENTER: // Center original image in the plot area
  864. $centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2);
  865. $centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2);
  866. $GLOBALS["copyfunc"]($this->img->img,$bkgimg,
  867. $centerx,$centery,
  868. 0,0,
  869. $bw,$bh,
  870. $bw,$bh);
  871. $this->StrokeFrame();
  872. break;
  873. default:
  874. JpGraphError::Raise(" Unknown background image layout");
  875. }
  876. $this->img->SetAngle($aa);
  877. }
  878. else {
  879. $aa = $this->img->SetAngle(0);
  880. $this->StrokeFrame();
  881. $this->img->SetAngle($aa);
  882. $this->img->PushColor($this->plotarea_color);
  883. // Note: To be consistent we really should take a possible shadow
  884. // into account. However, that causes some problem for the LinearScale class
  885. // since in the current design it does not have any links to class Graph which
  886. // means it has no way of compensating for the adjusted plotarea in case of a
  887. // shadow. So, until I redesign LinearScale we can't compensate for this.
  888. // So just set the two adjustment parameters to zero for now.
  889. $boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
  890. $adj = 0; //$this->doshadow ? $this->shadow_width : 0 ;
  891. $this->img->FilledRectangle($this->img->left_margin+$boxadj,
  892. $this->img->top_margin+$boxadj,
  893. $this->img->width-$this->img->right_margin-$adj-2*$boxadj,
  894. $this->img->height-$this->img->bottom_margin-$adj-2*$boxadj);
  895. $this->img->PopColor();
  896. }
  897. $this->img->SetAngle($aa);
  898. }
  899. function StrokePlotBox() {
  900. // Should we draw a box around the plot area?
  901. if( $this->boxed ) {
  902. $this->img->SetLineWeight($this->box_weight);
  903. $this->img->SetColor($this->box_color);
  904. $this->img->Rectangle(
  905. $this->img->left_margin,$this->img->top_margin,
  906. $this->img->width-$this->img->right_margin,
  907. $this->img->height-$this->img->bottom_margin);
  908. }
  909. }
  910. function StrokeTitles() {
  911. // Stroke title
  912. $this->title->Center($this->img->left_margin,$this->img->width-$this->img->right_margin,5);
  913. $this->title->Stroke($this->img);
  914. // ... and subtitle
  915. $this->subtitle->Center($this->img->left_margin,$this->img->width-$this->img->right_margin,
  916. 7+$this->title->GetFontHeight($this->img));
  917. $this->subtitle->Stroke($this->img);
  918. }
  919. function StrokeTexts() {
  920. // Stroke any user added text objects
  921. if( $this->texts != null ) {
  922. for($i=0; $i<count($this->texts); ++$i) {
  923. $this->texts[$i]->Stroke($this->img);
  924. }
  925. }
  926. }
  927. function DisplayClientSideaImageMapAreas() {
  928. // Debug stuff - display the outline of the image map areas
  929. foreach ($this->plots as $p) {
  930. $csim.= $p->GetCSIMareas();
  931. }
  932. $csim.= $this->legend->GetCSIMareas();
  933. if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
  934. $this->img->SetColor($this->csimcolor);
  935. for ($i=0; $i<count($coords[0]); $i++) {
  936. if ($coords[1][$i]=="poly") {
  937. preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
  938. $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
  939. for ($j=0; $j<count($pts[0]); $j++) {
  940. $this->img->LineTo($pts[1][$j],$pts[2][$j]);
  941. }
  942. } else if ($coords[1][$i]=="rect") {
  943. $pts = preg_split('/,/', $coords[2][$i]);
  944. $this->img->SetStartPoint($pts[0],$pts[1]);
  945. $this->img->LineTo($pts[2],$pts[1]);
  946. $this->img->LineTo($pts[2],$pts[3]);
  947. $this->img->LineTo($pts[0],$pts[3]);
  948. $this->img->LineTo($pts[0],$pts[1]);
  949. }
  950. }
  951. }
  952. }
  953. function AdjustSaturationBrightnessContrast() {
  954. // Adjust the brightness and contrast of the image
  955. if( $this->image_contr || $this->image_bright )
  956. $this->img->AdjBrightContrast($this->image_bright,$this->image_contr);
  957. if( $this->image_sat )
  958. $this->img->AdjSat($this->image_sat);
  959. }
  960. // Text scale offset in world coordinates
  961. function SetTextScaleOff($aOff) {
  962. $this->text_scale_off = $aOff;
  963. }
  964. // Get min and max values for all included plots
  965. function GetPlotsYMinMax(&$aPlots) {
  966. list($xmax,$max) = $aPlots[0]->Max();
  967. list($xmin,$min) = $aPlots[0]->Min();
  968. for($i=0; $i<count($aPlots); ++$i ) {
  969. list($xmax,$ymax)=$aPlots[$i]->Max();
  970. list($xmin,$ymin)=$aPlots[$i]->Min();
  971. if (!is_string($ymax) || $ymax != "") $max=max($max,$ymax);
  972. if (!is_string($ymin) || $ymin != "") $min=min($min,$ymin);
  973. }
  974. if( $min == "" ) $min = 0;
  975. if( $max == "" ) $max = 0;
  976. if( $min == 0 && $max == 0 ) {
  977. // Special case if all values are 0
  978. $min=0;$max=1;
  979. }
  980. return array($min,$max);
  981. }
  982. // Draw a frame around the image
  983. function StrokeFrame() {
  984. if( !$this->doframe ) return;
  985. if( $this->doshadow ) {
  986. $this->img->SetColor($this->frame_color);
  987. if( $this->background_image_type <= 1 )
  988. $c = $this->margin_color;
  989. else
  990. $c = false;
  991. $this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
  992. $c,$this->shadow_width);
  993. }
  994. else {
  995. $this->img->SetLineWeight($this->frame_weight);
  996. if( $this->background_image_type <= 1 ) {
  997. $this->img->SetColor($this->margin_color);
  998. $this->img->FilledRectangle(1,1,$this->img->width-2,$this->img->height-2);
  999. }
  1000. $this->img->SetColor($this->frame_color);
  1001. $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
  1002. }
  1003. }
  1004. } // Class
  1005. //===================================================
  1006. // CLASS TTF
  1007. // Description: Handle TTF font names
  1008. //===================================================
  1009. class TTF {
  1010. var $font_fam;
  1011. //---------------
  1012. // CONSTRUCTOR
  1013. function TTF() {
  1014. // Base file names for available fonts
  1015. $this->font_fam=array(
  1016. FF_COURIER => TTF_DIR."courier",
  1017. FF_VERDANA => TTF_DIR."verdana",
  1018. FF_TIMES => TTF_DIR."times",
  1019. FF_HANDWRT => TTF_DIR."handwriting",
  1020. FF_COMIC => TTF_DIR."comic",
  1021. FF_ARIAL => TTF_DIR."arial",
  1022. FF_BOOK => TTF_DIR."bookant");
  1023. }
  1024. //---------------
  1025. // PUBLIC METHODS
  1026. // Create the TTF file from the font specification
  1027. function File($fam,$style=FS_NORMAL) {
  1028. $f=$this->font_fam[$fam];
  1029. if( !$f ) JpGraphError::Raise(" Unknown TTF font family.");
  1030. switch( $style ) {
  1031. case FS_NORMAL:
  1032. break;
  1033. case FS_BOLD: $f .= "bd";
  1034. break;
  1035. case FS_ITALIC: $f .= "i";
  1036. break;
  1037. case FS_BOLDIT: $f .= "bi";
  1038. break;
  1039. default:
  1040. JpGraphError::Raise(" Unknown TTF Style.");
  1041. }
  1042. $f .= ".ttf";
  1043. // Check that file exist
  1044. if( !file_exists($f) )
  1045. JpGraphError::Raise(" Can't open font file \"$f\". Wrong directory?");
  1046. return $f;
  1047. }
  1048. } // Class
  1049. //===================================================
  1050. // CLASS LineProperty
  1051. // Description: Holds properties for a line
  1052. //===================================================
  1053. class LineProperty {
  1054. var $iWeight=1, $iColor="black",$iStyle="solid";
  1055. var $iShow=true;
  1056. //---------------
  1057. // PUBLIC METHODS
  1058. function SetColor($aColor) {
  1059. $this->iColor = $aColor;
  1060. }
  1061. function SetWeight($aWeight) {
  1062. $this->iWeight = $aWeight;
  1063. }
  1064. function SetStyle($aStyle) {
  1065. $this->iStyle = $aStyle;
  1066. }
  1067. function Show($aShow=true) {
  1068. $this->iShow=$aShow;
  1069. }
  1070. function Stroke($aImg,$aX1,$aY1,$aX2,$aY2) {
  1071. if( $this->iShow ) {
  1072. $aImg->SetColor($this->iColor);
  1073. $aImg->SetLineWeight($this->iWeight);
  1074. $aImg->SetLineStyle($this->iStyle);
  1075. $aImg->StyleLine($aX1,$aY1,$aX2,$aY2);
  1076. }
  1077. }
  1078. }
  1079. //===================================================
  1080. // CLASS Text
  1081. // Description: Arbitrary text object that can be added to the graph
  1082. //===================================================
  1083. class Text {
  1084. var $t,$x=0,$y=0,$halign="left",$valign="top",$color=array(0,0,0);
  1085. var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$hide=false,$dir=0;
  1086. var $boxed=false; // Should the text be boxed
  1087. var $paragraph_align="left";
  1088. //---------------
  1089. // CONSTRUCTOR
  1090. // Create new text at absolute pixel coordinates
  1091. function Text($aTxt="",$aXAbsPos=0,$aYAbsPos=0) {
  1092. $this->t = $aTxt;
  1093. $this->x = $aXAbsPos;
  1094. $this->y = $aYAbsPos;
  1095. }
  1096. //---------------
  1097. // PUBLIC METHODS
  1098. // Set the string in the text object
  1099. function Set($aTxt) {
  1100. $this->t = $aTxt;
  1101. }
  1102. // Specify the position and alignment for the text object
  1103. function Pos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
  1104. $this->x = $aXAbsPos;
  1105. $this->y = $aYAbsPos;
  1106. $this->halign = $aHAlign;
  1107. $this->valign = $aVAlign;
  1108. }
  1109. // Specify alignment for the text
  1110. function Align($aHAlign,$aVAlign="top") {
  1111. $this->halign = $aHAlign;
  1112. $this->valign = $aVAlign;
  1113. }
  1114. // Specifies the alignment for a multi line text
  1115. function ParagraphAlign($aAlign) {
  1116. $this->paragraph_align = $aAlign;
  1117. }
  1118. // Specify that the text should be boxed. fcolor=frame color, bcolor=border color,
  1119. // $shadow=drop shadow should be added around the text.
  1120. function SetBox($aFrameColor=array(255,255,255),$aBorderColor=array(0,0,0),$aShadow=false) {
  1121. if( $aFrameColor==false )
  1122. $this->boxed=false;
  1123. else
  1124. $this->boxed=true;
  1125. $this->fcolor=$aFrameColor;
  1126. $this->bcolor=$aBorderColor;
  1127. $this->shadow=$aShadow;
  1128. }
  1129. // Hide the text
  1130. function Hide($aHide=true) {
  1131. $this->hide=$aHide;
  1132. }
  1133. // This looks ugly since it's not a very orthogonal design
  1134. // but I added this "inverse" of Hide() to harmonize
  1135. // with some classes which I designed more recently (especially)
  1136. // jpgraph_gantt
  1137. function Show($aShow=true) {
  1138. $this->hide=!$aShow;
  1139. }
  1140. // Specify font
  1141. function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
  1142. $this->font_family=$aFamily;
  1143. $this->font_style=$aStyle;
  1144. $this->font_size=$aSize;
  1145. }
  1146. // Center the text between $left and $right coordinates
  1147. function Center($aLeft,$aRight,$aYAbsPos=false) {
  1148. $this->x = $aLeft + ($aRight-$aLeft )/2;
  1149. $this->halign = "center";
  1150. if( is_numeric($aYAbsPos) )
  1151. $this->y = $aYAbsPos;
  1152. }
  1153. // Set text color
  1154. function SetColor($aColor) {
  1155. $this->color = $aColor;
  1156. }
  1157. // Orientation of text. Note only TTF fonts can have an arbitrary angle
  1158. function SetOrientation($aDirection=0) {
  1159. if( is_numeric($aDirection) )
  1160. $this->dir=$aDirection;
  1161. elseif( $aDirection=="h" )
  1162. $this->dir = 0;
  1163. elseif( $aDirection=="v" )
  1164. $this->dir = 90;
  1165. else JpGraphError::Raise(" Invalid direction specified for text.");
  1166. }
  1167. // Total width of text
  1168. function GetWidth(&$aImg) {
  1169. $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  1170. return $aImg->GetTextWidth($this->t);
  1171. }
  1172. // Hight of font
  1173. function GetFontHeight(&$aImg) {
  1174. $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  1175. return $aImg->GetFontHeight();
  1176. }
  1177. function GetTextHeight(&$aImg) {
  1178. $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  1179. return $aImg->GetTextHeight($this->t);
  1180. }
  1181. // Display text in image
  1182. function Stroke(&$aImg,$x=-1,$y=-1) {
  1183. if( $x>-1 ) $this->x = $x;
  1184. if( $y>-1 ) $this->y = $y;
  1185. // If position been given as a fraction of the image size
  1186. // calculate the absolute position
  1187. if( $this->x < 1 ) $this->x *= $aImg->width;
  1188. if( $this->y < 1 ) $this->y *= $aImg->height;
  1189. $aImg->PushColor($this->color);
  1190. $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
  1191. $aImg->SetTextAlign($this->halign,$this->valign);
  1192. if( $this->boxed ) {
  1193. if( $this->fcolor=="nofill" ) $this->fcolor=false;
  1194. $aImg->StrokeBoxedText($this->x,$this->y,$this->t,
  1195. $this->dir,$this->fcolor,$this->bcolor,$this->shadow,
  1196. $this->paragraph_align);
  1197. }
  1198. else {
  1199. $aImg->StrokeText($this->x,$this->y,$this->t,$this->dir,
  1200. $this->paragraph_align);
  1201. }
  1202. $aImg->PopColor($this->color);
  1203. }
  1204. } // Class
  1205. //===================================================
  1206. // CLASS Grid
  1207. // Description: responsible for drawing grid lines in graph
  1208. //===================================================
  1209. class Grid {
  1210. var $img;
  1211. var $scale;
  1212. var $grid_color=array(196,196,196);
  1213. var $type="solid";
  1214. var $show=false, $showMinor=false,$weight=1;
  1215. //---------------
  1216. // CONSTRUCTOR
  1217. function Grid(&$aAxis) {
  1218. $this->scale = &$aAxis->scale;
  1219. $this->img = &$aAxis->img;
  1220. }
  1221. //---------------
  1222. // PUBLIC METHODS
  1223. function SetColor($aColor) {
  1224. $this->grid_color=$aColor;
  1225. }
  1226. function SetWeight($aWeight) {
  1227. $this->weight=$aWeight;
  1228. }
  1229. // Specify if grid should be dashed, dotted or solid
  1230. function SetLineStyle($aType) {
  1231. $this->type = $aType;
  1232. }
  1233. // Decide if both major and minor grid should be displayed
  1234. function Show($aShowMajor=true,$aShowMinor=false) {
  1235. $this->show=$aShowMajor;
  1236. $this->showMinor=$aShowMinor;
  1237. }
  1238. // Display the grid
  1239. function Stroke() {
  1240. if( $this->showMinor )
  1241. $this->DoStroke($this->scale->ticks->ticks_pos);
  1242. else
  1243. $this->DoStroke($this->scale->ticks->maj_ticks_pos);
  1244. }
  1245. //--------------
  1246. // Private methods
  1247. // Draw the grid
  1248. function DoStroke(&$aTicksPos) {
  1249. if( !$this->show )
  1250. return;
  1251. $this->img->SetColor($this->grid_color);
  1252. $this->img->SetLineWeight($this->weight);
  1253. $nbrgrids = count($aTicksPos);
  1254. if( $this->scale->type=="y" ) {
  1255. $xl=$this->img->left_margin;
  1256. $xr=$this->img->width-$this->img->right_margin;
  1257. for($i=0; $i<$nbrgrids; ++$i) {
  1258. $y=$aTicksPos[$i];
  1259. if( $this->type == "solid" )
  1260. $this->img->Line($xl,$y,$xr,$y);
  1261. elseif( $this->type == "dotted" )
  1262. $this->img->DashedLine($xl,$y,$xr,$y,1,6);
  1263. elseif( $this->type == "dashed" )
  1264. $this->img->DashedLine($xl,$y,$xr,$y,2,4);
  1265. elseif( $this->type == "longdashed" )
  1266. $this->img->DashedLine($xl,$y,$xr,$y,8,6);
  1267. }
  1268. }
  1269. if( $this->scale->type=="x" ) {
  1270. $yu=$this->img->top_margin;
  1271. $yl=$this->img->height-$this->img->bottom_margin;
  1272. $x=$aTicksPos[0];
  1273. $limit=$this->img->width-$this->img->right_margin;
  1274. $i=0;
  1275. // We must also test for limit since we might have
  1276. // an offset and the number of ticks is calculated with
  1277. // assumption offset==0 so we might end up drawing one
  1278. // to many gridlines
  1279. while( $x<=$limit && $i<count($aTicksPos)) {
  1280. $x=$aTicksPos[$i];
  1281. if( $this->type == "solid" )
  1282. $this->img->Line($x,$yl,$x,$yu);
  1283. elseif( $this->type == "dotted" )
  1284. $this->img->DashedLine($x,$yl,$x,$yu,1,6);
  1285. elseif( $this->type == "dashed" )
  1286. $this->img->DashedLine($x,$yl,$x,$yu,2,4);
  1287. elseif( $this->type == "longdashed" )
  1288. $this->img->DashedLine($x,$yl,$x,$yu,8,6);
  1289. ++$i;
  1290. }
  1291. }
  1292. return true;
  1293. }
  1294. } // Class
  1295. //===================================================
  1296. // CLASS Axis
  1297. // Description: Defines X and Y axis. Notes that at the
  1298. // moment the code is not really good since the axis on
  1299. // several occasion must know wheter it's an X or Y axis.
  1300. // This was a design decision to make the code easier to
  1301. // follow.
  1302. //===================================================
  1303. class Axis {
  1304. var $pos = false;
  1305. var $weight=1;
  1306. var $color=array(0,0,0),$label_color=array(0,0,0);
  1307. var $img=null,$scale=null;
  1308. var $hide=false;
  1309. var $ticks_label=false;
  1310. var $show_first_label=true;
  1311. var $label_step=1; // Used by a text axis to specify what multiple of major steps
  1312. // should be labeled.
  1313. var $tick_step=1;
  1314. var $labelPos=0; // Which side of the axis should the labels be?
  1315. var $title=null,$title_adjust,$title_margin,$title_side=SIDE_LEFT;
  1316. var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$label_angle=0;
  1317. var $tick_label_margin=6;
  1318. //---------------
  1319. // CONSTRUCTOR
  1320. function Axis(&$img,&$aScale,$color=array(0,0,0)) {
  1321. $this->img = &$img;
  1322. $this->scale = &$aScale;
  1323. $this->color = $color;
  1324. $this->title=new Text("");
  1325. if( $aScale->type=="y" ) {
  1326. $this->title_margin = 25;
  1327. $this->title_adjust="middle";
  1328. $this->title->SetOrientation(90);
  1329. $this->tick_label_margin=6;
  1330. $this->labelPos=SIDE_LEFT;
  1331. }
  1332. else {
  1333. $this->title_margin = 5;
  1334. $this->title_adjust="high";
  1335. $this->title->SetOrientation(0);
  1336. $this->tick_label_margin=3;
  1337. $this->labelPos=SIDE_DOWN;
  1338. }
  1339. }
  1340. //---------------
  1341. // PUBLIC METHODS
  1342. // Utility function to set the direction for tick marks
  1343. function SetTickDirection($aDir) {
  1344. $this->scale->ticks->SetDirection($aDir);
  1345. }
  1346. function SetLabelFormatString($aFormStr) {
  1347. $this->scale->ticks->SetLabelFormat($aFormStr);
  1348. }
  1349. function SetLabelFormatCallback($aFuncName) {
  1350. $this->scale->ticks->SetFormatCallback($aFuncName);
  1351. }
  1352. // Don't display the first label
  1353. function HideFirstTickLabel($aHide=false) {
  1354. $this->show_first_label=$aHide;
  1355. }
  1356. // Hide the axis
  1357. function Hide($aHide=true) {
  1358. $this->hide=$aHide;
  1359. }
  1360. // Weight of axis
  1361. function SetWeight($aWeight) {
  1362. $this->weight = $aWeight;
  1363. }
  1364. // Axis color
  1365. function SetColor($aColor,$aLabelColor=false) {
  1366. $this->color = $aColor;
  1367. if( !$aLabelColor ) $this->label_color = $aColor;
  1368. else $this->label_color = $aLabelColor;

Large files files are truncated, but you can click here to view the full file