1<?php 2//======================================================================= 3// File: GD_IMAGE.INC.PHP 4// Description: PHP Graph Plotting library. Low level image drawing routines 5// Created: 2001-01-08, refactored 2008-03-29 6// Ver: $Id: gd_image.inc.php 1922 2010-01-11 11:42:50Z ljp $ 7// 8// Copyright (c) Asial Corporation. All rights reserved. 9//======================================================================== 10 11require_once 'jpgraph_rgb.inc.php'; 12require_once 'jpgraph_ttf.inc.php'; 13require_once 'imageSmoothArc.php'; 14require_once 'jpgraph_errhandler.inc.php'; 15 16// Line styles 17define('LINESTYLE_SOLID',1); 18define('LINESTYLE_DOTTED',2); 19define('LINESTYLE_DASHED',3); 20define('LINESTYLE_LONGDASH',4); 21 22// The DEFAULT_GFORMAT sets the default graphic encoding format, i.e. 23// PNG, JPG or GIF depending on what is installed on the target system 24// in that order. 25if( !DEFINED("DEFAULT_GFORMAT") ) { 26 define("DEFAULT_GFORMAT","auto"); 27} 28 29//======================================================================== 30// CLASS Image 31// Description: The very coor image drawing class that encapsulates all 32// calls to the GD library 33// Note: The class used by the library is the decendant 34// class RotImage which extends the Image class with transparent 35// rotation. 36//========================================================================= 37class Image { 38 public $img=null; 39 public $rgb=null; 40 public $img_format; 41 public $ttf=null; 42 public $line_style=LINESTYLE_SOLID; 43 public $current_color,$current_color_name; 44 public $original_width=0, $original_height=0; 45 public $plotwidth=0,$plotheight=0; 46 47 // for __get, __set 48 private $_left_margin=30,$_right_margin=30,$_top_margin=20,$_bottom_margin=30; 49 //private $_plotwidth=0,$_plotheight=0; 50 private $_width=0, $_height=0; 51 private $_line_weight=1; 52 53 protected $expired=true; 54 protected $lastx=0, $lasty=0; 55 protected $obs_list=array(); 56 protected $font_size=12,$font_family=FF_DEFAULT, $font_style=FS_NORMAL; 57 protected $font_file=''; 58 protected $text_halign="left",$text_valign="bottom"; 59 protected $use_anti_aliasing=false; 60 protected $quality=null; 61 protected $colorstack=array(),$colorstackidx=0; 62 protected $canvascolor = 'white' ; 63 protected $langconv = null ; 64 protected $iInterlace=false; 65 protected $bbox_cache = array(); // STore the last found tetx bounding box 66 protected $ff_font0; 67 protected $ff_font0_bold; 68 protected $ff_font1; 69 protected $ff_font1_bold; 70 protected $ff_font2; 71 protected $ff_font2_bold; 72 73 74 //--------------- 75 // CONSTRUCTOR 76 function __construct($aWidth=0,$aHeight=0,$aFormat=DEFAULT_GFORMAT,$aSetAutoMargin=true) { 77 78 $this->original_width = $aWidth; 79 $this->original_height = $aHeight; 80 $this->CreateImgCanvas($aWidth, $aHeight); 81 82 if( $aSetAutoMargin ) { 83 $this->SetAutoMargin(); 84 } 85 86 if( !$this->SetImgFormat($aFormat) ) { 87 JpGraphError::RaiseL(25081,$aFormat);//("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]"); 88 } 89 $this->ttf = new TTF(); 90 $this->langconv = new LanguageConv(); 91 92 $this->ff_font0 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT0.gdf"); 93 $this->ff_font1 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1.gdf"); 94 $this->ff_font2 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2.gdf"); 95 $this->ff_font1_bold = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1-Bold.gdf"); 96 $this->ff_font2_bold = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2-Bold.gdf"); 97 } 98 99 // Enable interlacing in images 100 function SetInterlace($aFlg=true) { 101 $this->iInterlace=$aFlg; 102 } 103 104 // Should we use anti-aliasing. Note: This really slows down graphics! 105 function SetAntiAliasing($aFlg=true) { 106 $this->use_anti_aliasing = $aFlg; 107 if( function_exists('imageantialias') ) { 108 imageantialias($this->img,$aFlg); 109 } 110 else { 111 JpGraphError::RaiseL(25128);//('The function imageantialias() is not available in your PHP installation. Use the GD version that comes with PHP and not the standalone version.') 112 } 113 } 114 115 function GetAntiAliasing() { 116 return $this->use_anti_aliasing ; 117 } 118 119 function CreateRawCanvas($aWidth=0,$aHeight=0) { 120 121 $aWidth *= SUPERSAMPLING_SCALE; 122 $aHeight *= SUPERSAMPLING_SCALE; 123 124 if( $aWidth <= 1 || $aHeight <= 1 ) { 125 JpGraphError::RaiseL(25082,$aWidth,$aHeight);//("Illegal sizes specified for width or height when creating an image, (width=$aWidth, height=$aHeight)"); 126 } 127 128 $this->img = @imagecreatetruecolor($aWidth, $aHeight); 129 if( $this->img < 1 ) { 130 JpGraphError::RaiseL(25126); 131 //die("Can't create truecolor image. Check that you really have GD2 library installed."); 132 } 133 $this->SetAlphaBlending(); 134 135 if( $this->iInterlace ) { 136 imageinterlace($this->img,1); 137 } 138 if( $this->rgb != null ) { 139 $this->rgb->img = $this->img ; 140 } 141 else { 142 $this->rgb = new RGB($this->img); 143 } 144 } 145 146 function CloneCanvasH() { 147 $oldimage = $this->img; 148 $this->CreateRawCanvas($this->width,$this->height); 149 imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height); 150 return $oldimage; 151 } 152 153 function CreateImgCanvas($aWidth=0,$aHeight=0) { 154 155 $old = array($this->img,$this->width,$this->height); 156 157 $aWidth = round($aWidth); 158 $aHeight = round($aHeight); 159 160 $this->width=$aWidth; 161 $this->height=$aHeight; 162 163 164 if( $aWidth==0 || $aHeight==0 ) { 165 // We will set the final size later. 166 // Note: The size must be specified before any other 167 // img routines that stroke anything are called. 168 $this->img = null; 169 $this->rgb = null; 170 return $old; 171 } 172 173 $this->CreateRawCanvas($aWidth,$aHeight); 174 // Set canvas color (will also be the background color for a 175 // a pallett image 176 $this->SetColor($this->canvascolor); 177 $this->FilledRectangle(0,0,$this->width-1,$this->height-1); 178 179 return $old ; 180 } 181 182 function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) { 183 if( $aw === -1 ) { 184 $aw = $aWidth; 185 $ah = $aHeight; 186 $f = 'imagecopyresized'; 187 } 188 else { 189 $f = 'imagecopyresampled'; 190 } 191 $f($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah); 192 } 193 194 function Copy($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1) { 195 $this->CopyCanvasH($this->img,$fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight); 196 } 197 198 function CopyMerge($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1,$aMix=100) { 199 if( $aMix == 100 ) { 200 $this->CopyCanvasH($this->img,$fromImg, 201 $toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight); 202 } 203 else { 204 if( ($fromWidth != -1 && ($fromWidth != $toWidth)) || ($fromHeight != -1 && ($fromHeight != $fromHeight)) ) { 205 // Create a new canvas that will hold the re-scaled original from image 206 if( $toWidth <= 1 || $toHeight <= 1 ) { 207 JpGraphError::RaiseL(25083);//('Illegal image size when copying image. Size for copied to image is 1 pixel or less.'); 208 } 209 210 $tmpimg = @imagecreatetruecolor($toWidth, $toHeight); 211 212 if( $tmpimg < 1 ) { 213 JpGraphError::RaiseL(25084);//('Failed to create temporary GD canvas. Out of memory ?'); 214 } 215 $this->CopyCanvasH($tmpimg,$fromImg,0,0,0,0, 216 $toWidth,$toHeight,$fromWidth,$fromHeight); 217 $fromImg = $tmpimg; 218 } 219 imagecopymerge($this->img,$fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$aMix); 220 } 221 } 222 223 static function GetWidth($aImg) { 224 return imagesx($aImg); 225 } 226 227 static function GetHeight($aImg) { 228 return imagesy($aImg); 229 } 230 231 static function CreateFromString($aStr) { 232 $img = imagecreatefromstring($aStr); 233 if( $img === false ) { 234 JpGraphError::RaiseL(25085); 235 //('An image can not be created from the supplied string. It is either in a format not supported or the string is representing an corrupt image.'); 236 } 237 return $img; 238 } 239 240 function SetCanvasH($aHdl) { 241 $this->img = $aHdl; 242 $this->rgb->img = $aHdl; 243 } 244 245 function SetCanvasColor($aColor) { 246 $this->canvascolor = $aColor ; 247 } 248 249 function SetAlphaBlending($aFlg=true) { 250 ImageAlphaBlending($this->img,$aFlg); 251 } 252 253 function SetAutoMargin() { 254 $min_bm=5; 255 $lm = min(40,$this->width/7); 256 $rm = min(20,$this->width/10); 257 $tm = max(5,$this->height/7); 258 $bm = max($min_bm,$this->height/6); 259 $this->SetMargin($lm,$rm,$tm,$bm); 260 } 261 262 //--------------- 263 // PUBLIC METHODS 264 265 function SetFont($family,$style=FS_NORMAL,$size=10) { 266 $this->font_family=$family; 267 $this->font_style=$style; 268 $this->font_size=$size*SUPERSAMPLING_SCALE; 269 $this->font_file=''; 270 if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){ 271 ++$this->font_family; 272 } 273 if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file 274 275 // Check that this PHP has support for TTF fonts 276 if( !function_exists('imagettfbbox') ) { 277 // use internal font when php is configured without '--with-ttf' 278 $this->font_family = FF_FONT1; 279// JpGraphError::RaiseL(25087);//('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.'); 280 } else { 281 $this->font_file = $this->ttf->File($this->font_family,$this->font_style); 282 } 283 } 284 } 285 286 // Get the specific height for a text string 287 function GetTextHeight($txt="",$angle=0) { 288 $tmp = preg_split('/\n/',$txt); 289 $n = count($tmp); 290 $m=0; 291 for($i=0; $i< $n; ++$i) { 292 $m = max($m,strlen($tmp[$i])); 293 } 294 295 if( $this->font_family <= FF_FONT2+1 ) { 296 if( $angle==0 ) { 297 $h = imagefontheight($this->font_family); 298 if( $h === false ) { 299 JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); 300 } 301 302 return $n*$h; 303 } 304 else { 305 $w = @imagefontwidth($this->font_family); 306 if( $w === false ) { 307 JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); 308 } 309 310 return $m*$w; 311 } 312 } 313 else { 314 $bbox = $this->GetTTFBBox($txt,$angle); 315 return $bbox[1]-$bbox[5]+1; 316 } 317 } 318 319 // Estimate font height 320 function GetFontHeight($angle=0) { 321 $txt = "XOMg"; 322 return $this->GetTextHeight($txt,$angle); 323 } 324 325 // Approximate font width with width of letter "O" 326 function GetFontWidth($angle=0) { 327 $txt = 'O'; 328 return $this->GetTextWidth($txt,$angle); 329 } 330 331 // Get actual width of text in absolute pixels. Note that the width is the 332 // texts projected with onto the x-axis. Call with angle=0 to get the true 333 // etxt width. 334 function GetTextWidth($txt,$angle=0) { 335 336 $tmp = preg_split('/\n/',$txt); 337 $n = count($tmp); 338 if( $this->font_family <= FF_FONT2+1 ) { 339 340 $m=0; 341 for($i=0; $i < $n; ++$i) { 342 $l=strlen($tmp[$i]); 343 if( $l > $m ) { 344 $m = $l; 345 } 346 } 347 348 if( $angle==0 ) { 349 $w = @imagefontwidth($this->font_family); 350 if( $w === false ) { 351 JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); 352 } 353 return $m*$w; 354 } 355 else { 356 // 90 degrees internal so height becomes width 357 $h = @imagefontheight($this->font_family); 358 if( $h === false ) { 359 JpGraphError::RaiseL(25089);//('You have a misconfigured GD font support. The call to imagefontheight() fails.'); 360 } 361 return $n*$h; 362 } 363 } 364 else { 365 // For TTF fonts we must walk through a lines and find the 366 // widest one which we use as the width of the multi-line 367 // paragraph 368 $m=0; 369 for( $i=0; $i < $n; ++$i ) { 370 $bbox = $this->GetTTFBBox($tmp[$i],$angle); 371 $mm = $bbox[2] - $bbox[0]; 372 if( $mm > $m ) 373 $m = $mm; 374 } 375 return $m; 376 } 377 } 378 379 380 // Draw text with a box around it 381 function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black", 382 $shadowcolor=false,$paragraph_align="left", 383 $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) { 384 385 $oldx = $this->lastx; 386 $oldy = $this->lasty; 387 388 if( !is_numeric($dir) ) { 389 if( $dir=="h" ) $dir=0; 390 elseif( $dir=="v" ) $dir=90; 391 else JpGraphError::RaiseL(25090,$dir);//(" Unknown direction specified in call to StrokeBoxedText() [$dir]"); 392 } 393 394 if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) { 395 $width=$this->GetTextWidth($txt,$dir) ; 396 $height=$this->GetTextHeight($txt,$dir) ; 397 } 398 else { 399 $width=$this->GetBBoxWidth($txt,$dir) ; 400 $height=$this->GetBBoxHeight($txt,$dir) ; 401 } 402 403 $height += 2*$ymarg; 404 $width += 2*$xmarg; 405 406 if( $this->text_halign=="right" ) $x -= $width; 407 elseif( $this->text_halign=="center" ) $x -= $width/2; 408 409 if( $this->text_valign=="bottom" ) $y -= $height; 410 elseif( $this->text_valign=="center" ) $y -= $height/2; 411 412 $olda = $this->SetAngle(0); 413 414 if( $shadowcolor ) { 415 $this->PushColor($shadowcolor); 416 $this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth, 417 $x+$width+$dropwidth,$y+$height-$ymarg+$dropwidth, 418 $cornerradius); 419 $this->PopColor(); 420 $this->PushColor($fcolor); 421 $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg, 422 $x+$width,$y+$height-$ymarg, 423 $cornerradius); 424 $this->PopColor(); 425 $this->PushColor($bcolor); 426 $this->RoundedRectangle($x-$xmarg,$y-$ymarg, 427 $x+$width,$y+$height-$ymarg,$cornerradius); 428 $this->PopColor(); 429 } 430 else { 431 if( $fcolor ) { 432 $oc=$this->current_color; 433 $this->SetColor($fcolor); 434 $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius); 435 $this->current_color=$oc; 436 } 437 if( $bcolor ) { 438 $oc=$this->current_color; 439 $this->SetColor($bcolor); 440 $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius); 441 $this->current_color=$oc; 442 } 443 } 444 445 $h=$this->text_halign; 446 $v=$this->text_valign; 447 $this->SetTextAlign("left","top"); 448 449 $debug=false; 450 $this->StrokeText($x, $y, $txt, $dir, $paragraph_align,$debug); 451 452 $bb = array($x-$xmarg,$y+$height-$ymarg,$x+$width,$y+$height-$ymarg, 453 $x+$width,$y-$ymarg,$x-$xmarg,$y-$ymarg); 454 $this->SetTextAlign($h,$v); 455 456 $this->SetAngle($olda); 457 $this->lastx = $oldx; 458 $this->lasty = $oldy; 459 460 return $bb; 461 } 462 463 // Draw text with a box around it. This time the box will be rotated 464 // with the text. The previous method will just make a larger enough non-rotated 465 // box to hold the text inside. 466 function StrokeBoxedText2($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black", 467 $shadowcolor=false,$paragraph_align="left", 468 $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) { 469 470 // This version of boxed text will stroke a rotated box round the text 471 // thta will follow the angle of the text. 472 // This has two implications: 473 // 1) This methos will only support TTF fonts 474 // 2) The only two alignment that makes sense are centered or baselined 475 476 if( $this->font_family <= FF_FONT2+1 ) { 477 JpGraphError::RaiseL(25131);//StrokeBoxedText2() Only support TTF fonts and not built in bitmap fonts 478 } 479 480 $oldx = $this->lastx; 481 $oldy = $this->lasty; 482 $dir = $this->NormAngle($dir); 483 484 if( !is_numeric($dir) ) { 485 if( $dir=="h" ) $dir=0; 486 elseif( $dir=="v" ) $dir=90; 487 else JpGraphError::RaiseL(25090,$dir);//(" Unknown direction specified in call to StrokeBoxedText() [$dir]"); 488 } 489 490 $width=$this->GetTextWidth($txt,0) + 2*$xmarg; 491 $height=$this->GetTextHeight($txt,0) + 2*$ymarg ; 492 $rect_width=$this->GetBBoxWidth($txt,$dir) ; 493 $rect_height=$this->GetBBoxHeight($txt,$dir) ; 494 495 $baseline_offset = $this->bbox_cache[1]-1; 496 497 if( $this->text_halign=="center" ) { 498 if( $dir >= 0 && $dir <= 90 ) { 499 500 $x -= $rect_width/2; 501 $x += sin($dir*M_PI/180)*$height; 502 $y += $rect_height/2; 503 504 } elseif( $dir >= 270 && $dir <= 360 ) { 505 506 $x -= $rect_width/2; 507 $y -= $rect_height/2; 508 $y += cos($dir*M_PI/180)*$height; 509 510 } elseif( $dir >= 90 && $dir <= 180 ) { 511 512 $x += $rect_width/2; 513 $y += $rect_height/2; 514 $y += cos($dir*M_PI/180)*$height; 515 516 } 517 else { 518 // $dir > 180 && $dir < 270 519 $x += $rect_width/2; 520 $x += sin($dir*M_PI/180)*$height; 521 $y -= $rect_height/2; 522 } 523 } 524 525 // Rotate the box around this point 526 $this->SetCenter($x,$y); 527 $olda = $this->SetAngle(-$dir); 528 529 // We need to use adjusted coordinats for the box to be able 530 // to draw the box below the baseline. This cannot be done before since 531 // the rotating point must be the original x,y since that is arounbf the 532 // point where the text will rotate and we cannot change this since 533 // that is where the GD/GreeType will rotate the text 534 535 536 // For smaller <14pt font we need to do some additional 537 // adjustments to make it look good 538 if( $this->font_size < 14 ) { 539 $x -= 2; 540 $y += 2; 541 } 542 else { 543 // $y += $baseline_offset; 544 } 545 546 if( $shadowcolor ) { 547 $this->PushColor($shadowcolor); 548 $this->FilledRectangle($x-$xmarg+$dropwidth,$y+$ymarg+$dropwidth-$height, 549 $x+$width+$dropwidth,$y+$ymarg+$dropwidth); 550 //$cornerradius); 551 $this->PopColor(); 552 $this->PushColor($fcolor); 553 $this->FilledRectangle($x-$xmarg, $y+$ymarg-$height, 554 $x+$width, $y+$ymarg); 555 //$cornerradius); 556 $this->PopColor(); 557 $this->PushColor($bcolor); 558 $this->Rectangle($x-$xmarg,$y+$ymarg-$height, 559 $x+$width,$y+$ymarg); 560 //$cornerradius); 561 $this->PopColor(); 562 } 563 else { 564 if( $fcolor ) { 565 $oc=$this->current_color; 566 $this->SetColor($fcolor); 567 $this->FilledRectangle($x-$xmarg,$y+$ymarg-$height,$x+$width,$y+$ymarg);//,$cornerradius); 568 $this->current_color=$oc; 569 } 570 if( $bcolor ) { 571 $oc=$this->current_color; 572 $this->SetColor($bcolor); 573 $this->Rectangle($x-$xmarg,$y+$ymarg-$height,$x+$width,$y+$ymarg);//,$cornerradius); 574 $this->current_color=$oc; 575 } 576 } 577 578 if( $this->font_size < 14 ) { 579 $x += 2; 580 $y -= 2; 581 } 582 else { 583 584 // Restore the original y before we stroke the text 585 // $y -= $baseline_offset; 586 587 } 588 589 $this->SetCenter(0,0); 590 $this->SetAngle($olda); 591 592 $h=$this->text_halign; 593 $v=$this->text_valign; 594 if( $this->text_halign == 'center') { 595 $this->SetTextAlign('center','basepoint'); 596 } 597 else { 598 $this->SetTextAlign('basepoint','basepoint'); 599 } 600 601 $debug=false; 602 $this->StrokeText($x, $y, $txt, $dir, $paragraph_align,$debug); 603 604 $bb = array($x-$xmarg, $y+$height-$ymarg, 605 $x+$width, $y+$height-$ymarg, 606 $x+$width, $y-$ymarg, 607 $x-$xmarg, $y-$ymarg); 608 609 $this->SetTextAlign($h,$v); 610 $this->SetAngle($olda); 611 612 $this->lastx = $oldx; 613 $this->lasty = $oldy; 614 615 return $bb; 616 } 617 618 // Set text alignment 619 function SetTextAlign($halign,$valign="bottom") { 620 $this->text_halign=$halign; 621 $this->text_valign=$valign; 622 } 623 624 function _StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,&$aBoundingBox,$aDebug=false) { 625 626 if( is_numeric($dir) && $dir!=90 && $dir!=0) 627 JpGraphError::RaiseL(25091);//(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead."); 628 629 $h=$this->GetTextHeight($txt); 630 $fh=$this->GetFontHeight(); 631 $w=$this->GetTextWidth($txt); 632 633 if( $this->text_halign=="right") { 634 $x -= $dir==0 ? $w : $h; 635 } 636 elseif( $this->text_halign=="center" ) { 637 // For center we subtract 1 pixel since this makes the middle 638 // be prefectly in the middle 639 $x -= $dir==0 ? $w/2-1 : $h/2; 640 } 641 if( $this->text_valign=="top" ) { 642 $y += $dir==0 ? $h : $w; 643 } 644 elseif( $this->text_valign=="center" ) { 645 $y += $dir==0 ? $h/2 : $w/2; 646 } 647 648 $use_font = $this->font_family; 649 650 if( $dir==90 ) { 651 imagestringup($this->img,$use_font,$x,$y,$txt,$this->current_color); 652 $aBoundingBox = array(round($x),round($y),round($x),round($y-$w),round($x+$h),round($y-$w),round($x+$h),round($y)); 653 if( $aDebug ) { 654 // Draw bounding box 655 $this->PushColor('green'); 656 $this->Polygon($aBoundingBox,true); 657 $this->PopColor(); 658 } 659 } 660 else { 661 if( preg_match('/\n/',$txt) ) { 662 $tmp = preg_split('/\n/',$txt); 663 for($i=0; $i < count($tmp); ++$i) { 664 $w1 = $this->GetTextWidth($tmp[$i]); 665 if( $paragraph_align=="left" ) { 666 imagestring($this->img,$use_font,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); 667 } 668 elseif( $paragraph_align=="right" ) { 669 imagestring($this->img,$use_font,$x+($w-$w1),$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); 670 } 671 else { 672 imagestring($this->img,$use_font,$x+$w/2-$w1/2,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); 673 } 674 } 675 } 676 else { 677 //Put the text 678 imagestring($this->img,$use_font,$x,$y-$h+1,$txt,$this->current_color); 679 } 680 if( $aDebug ) { 681 // Draw the bounding rectangle and the bounding box 682 $p1 = array(round($x),round($y),round($x),round($y-$h),round($x+$w),round($y-$h),round($x+$w),round($y)); 683 684 // Draw bounding box 685 $this->PushColor('green'); 686 $this->Polygon($p1,true); 687 $this->PopColor(); 688 689 } 690 $aBoundingBox=array(round($x),round($y),round($x),round($y-$h),round($x+$w),round($y-$h),round($x+$w),round($y)); 691 } 692 } 693 694 function AddTxtCR($aTxt) { 695 // If the user has just specified a '\n' 696 // instead of '\n\t' we have to add '\r' since 697 // the width will be too muchy otherwise since when 698 // we print we stroke the individually lines by hand. 699 $e = explode("\n",$aTxt); 700 $n = count($e); 701 for($i=0; $i<$n; ++$i) { 702 $e[$i]=str_replace("\r","",$e[$i]); 703 } 704 return implode("\n\r",$e); 705 } 706 707 function NormAngle($a) { 708 // Normalize angle in degrees 709 // Normalize angle to be between 0-360 710 while( $a > 360 ) 711 $a -= 360; 712 while( $a < -360 ) 713 $a += 360; 714 if( $a < 0 ) 715 $a = 360 + $a; 716 return $a; 717 } 718 719 function imagettfbbox_fixed($size, $angle, $fontfile, $text) { 720 721 722 if( ! USE_LIBRARY_IMAGETTFBBOX ) { 723 724 $bbox = @imagettfbbox($size, $angle, $fontfile, $text); 725 if( $bbox === false ) { 726 JpGraphError::RaiseL(25092,$this->font_file); 727 //("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library."); 728 } 729 $this->bbox_cache = $bbox; 730 return $bbox; 731 } 732 733 // The built in imagettfbbox is buggy for angles != 0 so 734 // we calculate this manually by getting the bounding box at 735 // angle = 0 and then rotate the bounding box manually 736 $bbox = @imagettfbbox($size, 0, $fontfile, $text); 737 if( $bbox === false ) { 738 JpGraphError::RaiseL(25092,$this->font_file); 739 //("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library."); 740 } 741 742 $angle = $this->NormAngle($angle); 743 744 $a = $angle*M_PI/180; 745 $ca = cos($a); 746 $sa = sin($a); 747 $ret = array(); 748 749 // We always add 1 pixel to the left since the left edge of the bounding 750 // box is sometimes coinciding with the first pixel of the text 751 //$bbox[0] -= 1; 752 //$bbox[6] -= 1; 753 754 // For roatated text we need to add extra width for rotated 755 // text since the kerning and stroking of the TTF is not the same as for 756 // text at a 0 degree angle 757 758 if( $angle > 0.001 && abs($angle-360) > 0.001 ) { 759 $h = abs($bbox[7]-$bbox[1]); 760 $w = abs($bbox[2]-$bbox[0]); 761 762 $bbox[0] -= 2; 763 $bbox[6] -= 2; 764 // The width is underestimated so compensate for that 765 $bbox[2] += round($w*0.06); 766 $bbox[4] += round($w*0.06); 767 768 // and we also need to compensate with increased height 769 $bbox[5] -= round($h*0.1); 770 $bbox[7] -= round($h*0.1); 771 772 if( $angle > 90 ) { 773 // For angles > 90 we also need to extend the height further down 774 // by the baseline since that is also one more problem 775 $bbox[1] += round($h*0.15); 776 $bbox[3] += round($h*0.15); 777 778 // and also make it slighty less height 779 $bbox[7] += round($h*0.05); 780 $bbox[5] += round($h*0.05); 781 782 // And we need to move the box slightly top the rright (from a tetx perspective) 783 $bbox[0] += round($w*0.02); 784 $bbox[6] += round($w*0.02); 785 786 if( $angle > 180 ) { 787 // And we need to move the box slightly to the left (from a text perspective) 788 $bbox[0] -= round($w*0.02); 789 $bbox[6] -= round($w*0.02); 790 $bbox[2] -= round($w*0.02); 791 $bbox[4] -= round($w*0.02); 792 793 } 794 795 } 796 for($i = 0; $i < 7; $i += 2) { 797 $ret[$i] = round($bbox[$i] * $ca + $bbox[$i+1] * $sa); 798 $ret[$i+1] = round($bbox[$i+1] * $ca - $bbox[$i] * $sa); 799 } 800 $this->bbox_cache = $ret; 801 return $ret; 802 } 803 else { 804 $this->bbox_cache = $bbox; 805 return $bbox; 806 } 807 } 808 809 // Deprecated 810 function GetTTFBBox($aTxt,$aAngle=0) { 811 $bbox = $this->imagettfbbox_fixed($this->font_size,$aAngle,$this->font_file,$aTxt); 812 return $bbox; 813 } 814 815 function GetBBoxTTF($aTxt,$aAngle=0) { 816 // Normalize the bounding box to become a minimum 817 // enscribing rectangle 818 819 $aTxt = $this->AddTxtCR($aTxt); 820 821 if( !is_readable($this->font_file) ) { 822 JpGraphError::RaiseL(25093,$this->font_file); 823 //('Can not read font file ('.$this->font_file.') in call to Image::GetBBoxTTF. Please make sure that you have set a font before calling this method and that the font is installed in the TTF directory.'); 824 } 825 $bbox = $this->imagettfbbox_fixed($this->font_size,$aAngle,$this->font_file,$aTxt); 826 827 if( $aAngle==0 ) return $bbox; 828 829 if( $aAngle >= 0 ) { 830 if( $aAngle <= 90 ) { //<=0 831 $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1], 832 $bbox[2],$bbox[5],$bbox[6],$bbox[5]); 833 } 834 elseif( $aAngle <= 180 ) { //<= 2 835 $bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7], 836 $bbox[0],$bbox[3],$bbox[4],$bbox[3]); 837 } 838 elseif( $aAngle <= 270 ) { //<= 3 839 $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5], 840 $bbox[6],$bbox[1],$bbox[2],$bbox[1]); 841 } 842 else { 843 $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], 844 $bbox[4],$bbox[7],$bbox[0],$bbox[7]); 845 } 846 } 847 elseif( $aAngle < 0 ) { 848 if( $aAngle <= -270 ) { // <= -3 849 $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1], 850 $bbox[2],$bbox[5],$bbox[6],$bbox[5]); 851 } 852 elseif( $aAngle <= -180 ) { // <= -2 853 $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], 854 $bbox[4],$bbox[7],$bbox[0],$bbox[7]); 855 } 856 elseif( $aAngle <= -90 ) { // <= -1 857 $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5], 858 $bbox[6],$bbox[1],$bbox[2],$bbox[1]); 859 } 860 else { 861 $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], 862 $bbox[4],$bbox[7],$bbox[0],$bbox[7]); 863 } 864 } 865 return $bbox; 866 } 867 868 function GetBBoxHeight($aTxt,$aAngle=0) { 869 $box = $this->GetBBoxTTF($aTxt,$aAngle); 870 return abs($box[7]-$box[1]); 871 } 872 873 function GetBBoxWidth($aTxt,$aAngle=0) { 874 $box = $this->GetBBoxTTF($aTxt,$aAngle); 875 return $box[2]-$box[0]+1; 876 } 877 878 879 function _StrokeTTF($x,$y,$txt,$dir,$paragraph_align,&$aBoundingBox,$debug=false) { 880 881 // Setup default inter line margin for paragraphs to be 882 // 3% of the font height. 883 $ConstLineSpacing = 0.03 ; 884 885 // Remember the anchor point before adjustment 886 if( $debug ) { 887 $ox=$x; 888 $oy=$y; 889 } 890 891 if( !preg_match('/\n/',$txt) || ($dir>0 && preg_match('/\n/',$txt)) ) { 892 // Format a single line 893 894 $txt = $this->AddTxtCR($txt); 895 $bbox=$this->GetBBoxTTF($txt,$dir); 896 $width = $this->GetBBoxWidth($txt,$dir); 897 $height = $this->GetBBoxHeight($txt,$dir); 898 899 // The special alignment "basepoint" is mostly used internally 900 // in the library. This will put the anchor position at the left 901 // basepoint of the tetx. This is the default anchor point for 902 // TTF text. 903 904 if( $this->text_valign != 'basepoint' ) { 905 // Align x,y ot lower left corner of bbox 906 907 908 if( $this->text_halign=='right' ) { 909 $x -= $width; 910 $x -= $bbox[0]; 911 } 912 elseif( $this->text_halign=='center' ) { 913 $x -= $width/2; 914 $x -= $bbox[0]; 915 } 916 elseif( $this->text_halign=='baseline' ) { 917 // This is only support for text at 90 degree !! 918 // Do nothing the text is drawn at baseline by default 919 } 920 921 if( $this->text_valign=='top' ) { 922 $y -= $bbox[1]; // Adjust to bottom of text 923 $y += $height; 924 } 925 elseif( $this->text_valign=='center' ) { 926 $y -= $bbox[1]; // Adjust to bottom of text 927 $y += $height/2; 928 } 929 elseif( $this->text_valign=='baseline' ) { 930 // This is only support for text at 0 degree !! 931 // Do nothing the text is drawn at baseline by default 932 } 933 } 934 ImageTTFText ($this->img, $this->font_size, $dir, $x, $y, 935 $this->current_color,$this->font_file,$txt); 936 937 // Calculate and return the co-ordinates for the bounding box 938 $box = $this->imagettfbbox_fixed($this->font_size,$dir,$this->font_file,$txt); 939 $p1 = array(); 940 941 for($i=0; $i < 4; ++$i) { 942 $p1[] = round($box[$i*2]+$x); 943 $p1[] = round($box[$i*2+1]+$y); 944 } 945 $aBoundingBox = $p1; 946 947 // Debugging code to highlight the bonding box and bounding rectangle 948 // For text at 0 degrees the bounding box and bounding rectangle are the 949 // same 950 if( $debug ) { 951 // Draw the bounding rectangle and the bounding box 952 953 $p = array(); 954 $p1 = array(); 955 956 for($i=0; $i < 4; ++$i) { 957 $p[] = $bbox[$i*2]+$x ; 958 $p[] = $bbox[$i*2+1]+$y; 959 $p1[] = $box[$i*2]+$x ; 960 $p1[] = $box[$i*2+1]+$y ; 961 } 962 963 // Draw bounding box 964 $this->PushColor('green'); 965 $this->Polygon($p1,true); 966 $this->PopColor(); 967 968 // Draw bounding rectangle 969 $this->PushColor('darkgreen'); 970 $this->Polygon($p,true); 971 $this->PopColor(); 972 973 // Draw a cross at the anchor point 974 $this->PushColor('red'); 975 $this->Line($ox-15,$oy,$ox+15,$oy); 976 $this->Line($ox,$oy-15,$ox,$oy+15); 977 $this->PopColor(); 978 } 979 } 980 else { 981 // Format a text paragraph 982 $fh=$this->GetFontHeight(); 983 984 // Line margin is 25% of font height 985 $linemargin=round($fh*$ConstLineSpacing); 986 $fh += $linemargin; 987 $w=$this->GetTextWidth($txt); 988 989 $y -= $linemargin/2; 990 $tmp = preg_split('/\n/',$txt); 991 $nl = count($tmp); 992 $h = $nl * $fh; 993 994 if( $this->text_halign=='right') { 995 $x -= $dir==0 ? $w : $h; 996 } 997 elseif( $this->text_halign=='center' ) { 998 $x -= $dir==0 ? $w/2 : $h/2; 999 } 1000 1001 if( $this->text_valign=='top' ) { 1002 $y += $dir==0 ? $h : $w; 1003 } 1004 elseif( $this->text_valign=='center' ) { 1005 $y += $dir==0 ? $h/2 : $w/2; 1006 } 1007 1008 // Here comes a tricky bit. 1009 // Since we have to give the position for the string at the 1010 // baseline this means thaht text will move slightly up 1011 // and down depending on any of it's character descend below 1012 // the baseline, for example a 'g'. To adjust the Y-position 1013 // we therefore adjust the text with the baseline Y-offset 1014 // as used for the current font and size. This will keep the 1015 // baseline at a fixed positoned disregarding the actual 1016 // characters in the string. 1017 $standardbox = $this->GetTTFBBox('Gg',$dir); 1018 $yadj = $standardbox[1]; 1019 $xadj = $standardbox[0]; 1020 $aBoundingBox = array(); 1021 for($i=0; $i < $nl; ++$i) { 1022 $wl = $this->GetTextWidth($tmp[$i]); 1023 $bbox = $this->GetTTFBBox($tmp[$i],$dir); 1024 if( $paragraph_align=='left' ) { 1025 $xl = $x; 1026 } 1027 elseif( $paragraph_align=='right' ) { 1028 $xl = $x + ($w-$wl); 1029 } 1030 else { 1031 // Center 1032 $xl = $x + $w/2 - $wl/2 ; 1033 } 1034 1035 // In theory we should adjust with full pre-lead to get the lines 1036 // lined up but this doesn't look good so therfore we only adjust with 1037 // half th pre-lead 1038 $xl -= $bbox[0]/2; 1039 $yl = $y - $yadj; 1040 //$xl = $xl- $xadj; 1041 ImageTTFText($this->img, $this->font_size, $dir, $xl, $yl-($h-$fh)+$fh*$i, 1042 $this->current_color,$this->font_file,$tmp[$i]); 1043 1044 // echo "xl=$xl,".$tmp[$i]." <br>"; 1045 if( $debug ) { 1046 // Draw the bounding rectangle around each line 1047 $box=@ImageTTFBBox($this->font_size,$dir,$this->font_file,$tmp[$i]); 1048 $p = array(); 1049 for($j=0; $j < 4; ++$j) { 1050 $p[] = $bbox[$j*2]+$xl; 1051 $p[] = $bbox[$j*2+1]+$yl-($h-$fh)+$fh*$i; 1052 } 1053 1054 // Draw bounding rectangle 1055 $this->PushColor('darkgreen'); 1056 $this->Polygon($p,true); 1057 $this->PopColor(); 1058 } 1059 } 1060 1061 // Get the bounding box 1062 $bbox = $this->GetBBoxTTF($txt,$dir); 1063 for($j=0; $j < 4; ++$j) { 1064 $bbox[$j*2]+= round($x); 1065 $bbox[$j*2+1]+= round($y - ($h-$fh) - $yadj); 1066 } 1067 $aBoundingBox = $bbox; 1068 1069 if( $debug ) { 1070 // Draw a cross at the anchor point 1071 $this->PushColor('red'); 1072 $this->Line($ox-25,$oy,$ox+25,$oy); 1073 $this->Line($ox,$oy-25,$ox,$oy+25); 1074 $this->PopColor(); 1075 } 1076 1077 } 1078 } 1079 1080 function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) { 1081 1082 $x = round($x); 1083 $y = round($y); 1084 1085 // Do special language encoding 1086 $txt = $this->langconv->Convert($txt,$this->font_family); 1087 1088 if( !is_numeric($dir) ) { 1089 JpGraphError::RaiseL(25094);//(" Direction for text most be given as an angle between 0 and 90."); 1090 } 1091 1092 if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) { 1093 $this->_StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,$boundingbox,$debug); 1094 } 1095 elseif( $this->font_family >= _FIRST_FONT && $this->font_family <= _LAST_FONT) { 1096 $this->_StrokeTTF($x,$y,$txt,$dir,$paragraph_align,$boundingbox,$debug); 1097 } 1098 else { 1099 JpGraphError::RaiseL(25095);//(" Unknown font font family specification. "); 1100 } 1101 return $boundingbox; 1102 } 1103 1104 function SetMargin($lm,$rm,$tm,$bm) { 1105 1106 $this->left_margin=$lm; 1107 $this->right_margin=$rm; 1108 $this->top_margin=$tm; 1109 $this->bottom_margin=$bm; 1110 1111 $this->plotwidth = $this->width - $this->left_margin - $this->right_margin; 1112 $this->plotheight = $this->height - $this->top_margin - $this->bottom_margin; 1113 1114 if( $this->width > 0 && $this->height > 0 ) { 1115 if( $this->plotwidth < 0 || $this->plotheight < 0 ) { 1116 JpGraphError::RaiseL(25130, $this->plotwidth, $this->plotheight); 1117 //JpGraphError::raise("To small plot area. ($lm,$rm,$tm,$bm : $this->plotwidth x $this->plotheight). With the given image size and margins there is to little space left for the plot. Increase the plot size or reduce the margins."); 1118 } 1119 } 1120 } 1121 1122 function SetTransparent($color) { 1123 imagecolortransparent ($this->img,$this->rgb->allocate($color)); 1124 } 1125 1126 function SetColor($color,$aAlpha=0) { 1127 $this->current_color_name = $color; 1128 $this->current_color=$this->rgb->allocate($color,$aAlpha); 1129 if( $this->current_color == -1 ) { 1130 $tc=imagecolorstotal($this->img); 1131 JpGraphError::RaiseL(25096); 1132 //("Can't allocate any more colors. Image has already allocated maximum of <b>$tc colors</b>. This might happen if you have anti-aliasing turned on together with a background image or perhaps gradient fill since this requires many, many colors. Try to turn off anti-aliasing. If there is still a problem try downgrading the quality of the background image to use a smaller pallete to leave some entries for your graphs. You should try to limit the number of colors in your background image to 64. If there is still problem set the constant DEFINE(\"USE_APPROX_COLORS\",true); in jpgraph.php This will use approximative colors when the palette is full. Unfortunately there is not much JpGraph can do about this since the palette size is a limitation of current graphic format and what the underlying GD library suppports."); 1133 } 1134 return $this->current_color; 1135 } 1136 1137 function PushColor($color) { 1138 if( $color != "" ) { 1139 $this->colorstack[$this->colorstackidx]=$this->current_color_name; 1140 $this->colorstack[$this->colorstackidx+1]=$this->current_color; 1141 $this->colorstackidx+=2; 1142 $this->SetColor($color); 1143 } 1144 else { 1145 JpGraphError::RaiseL(25097);//("Color specified as empty string in PushColor()."); 1146 } 1147 } 1148 1149 function PopColor() { 1150 if( $this->colorstackidx < 1 ) { 1151 JpGraphError::RaiseL(25098);//(" Negative Color stack index. Unmatched call to PopColor()"); 1152 } 1153 $this->current_color=$this->colorstack[--$this->colorstackidx]; 1154 $this->current_color_name=$this->colorstack[--$this->colorstackidx]; 1155 } 1156 1157 1158 function SetLineWeight($weight) { 1159 $old = $this->line_weight; 1160 imagesetthickness($this->img,$weight); 1161 $this->line_weight = $weight; 1162 return $old; 1163 } 1164 1165 function SetStartPoint($x,$y) { 1166 $this->lastx=round($x); 1167 $this->lasty=round($y); 1168 } 1169 1170 function Arc($cx,$cy,$w,$h,$s,$e) { 1171 // GD Arc doesn't like negative angles 1172 while( $s < 0) $s += 360; 1173 while( $e < 0) $e += 360; 1174 imagearc($this->img,round($cx),round($cy),round($w),round($h),$s,$e,$this->current_color); 1175 } 1176 1177 function FilledArc($xc,$yc,$w,$h,$s,$e,$style='') { 1178 $s = round($s); 1179 $e = round($e); 1180 while( $s < 0 ) $s += 360; 1181 while( $e < 0 ) $e += 360; 1182 if( $style=='' ) 1183 $style=IMG_ARC_PIE; 1184 if( abs($s-$e) > 0 ) { 1185 imagefilledarc($this->img,round($xc),round($yc),round($w),round($h),$s,$e,$this->current_color,$style); 1186// $this->DrawImageSmoothArc($this->img,round($xc),round($yc),round($w),round($h),$s,$e,$this->current_color,$style); 1187 } 1188 } 1189 1190 function FilledCakeSlice($cx,$cy,$w,$h,$s,$e) { 1191 $this->CakeSlice($cx,$cy,$w,$h,$s,$e,$this->current_color_name); 1192 } 1193 1194 function CakeSlice($xc,$yc,$w,$h,$s,$e,$fillcolor="",$arccolor="") { 1195 $s = round($s); $e = round($e); 1196 $w = round($w); $h = round($h); 1197 $xc = round($xc); $yc = round($yc); 1198 if( $s == $e ) { 1199 // A full circle. We draw this a plain circle 1200 $this->PushColor($fillcolor); 1201 imagefilledellipse($this->img,$xc,$yc,2*$w,2*$h,$this->current_color); 1202 1203 // If antialiasing is used then we often don't have any color no the surrounding 1204 // arc. So, we need to check for this special case so we don't send an empty 1205 // color to the push function. In this case we use the fill color for the arc as well 1206 if( $arccolor != '' ) { 1207 $this->PopColor(); 1208 $this->PushColor($arccolor); 1209 } 1210 imageellipse($this->img,$xc,$yc,2*$w,2*$h,$this->current_color); 1211 $this->Line($xc,$yc,cos($s*M_PI/180)*$w+$xc,$yc+sin($s*M_PI/180)*$h); 1212 $this->PopColor(); 1213 } 1214 else { 1215 $this->PushColor($fillcolor); 1216 $this->FilledArc($xc,$yc,2*$w,2*$h,$s,$e); 1217 $this->PopColor(); 1218 if( $arccolor != "" ) { 1219 $this->PushColor($arccolor); 1220 // We add 2 pixels to make the Arc() better aligned with 1221 // the filled arc. 1222 imagefilledarc($this->img,$xc,$yc,2*$w,2*$h,$s,$e,$this->current_color,IMG_ARC_NOFILL | IMG_ARC_EDGED ) ; 1223 $this->PopColor(); 1224 } 1225 } 1226 } 1227 1228 function Ellipse($xc,$yc,$w,$h) { 1229 $this->Arc($xc,$yc,$w,$h,0,360); 1230 } 1231 1232 function Circle($xc,$yc,$r) { 1233 imageellipse($this->img,round($xc),round($yc),$r*2,$r*2,$this->current_color); 1234// $this->DrawImageSmoothArc($this->img,round($xc),round($yc),$r*2+1,$r*2+1,0,360,$this->current_color); 1235// $this->imageSmoothCircle($this->img, round($xc),round($yc), $r*2+1, $this->current_color); 1236 } 1237 1238 function FilledCircle($xc,$yc,$r) { 1239 imagefilledellipse($this->img,round($xc),round($yc),2*$r,2*$r,$this->current_color); 1240// $this->DrawImageSmoothArc($this->img, round($xc), round($yc), 2*$r, 2*$r, 0, 360, $this->current_color); 1241 } 1242 1243 // Linear Color InterPolation 1244 function lip($f,$t,$p) { 1245 $p = round($p,1); 1246 $r = $f[0] + ($t[0]-$f[0])*$p; 1247 $g = $f[1] + ($t[1]-$f[1])*$p; 1248 $b = $f[2] + ($t[2]-$f[2])*$p; 1249 return array($r,$g,$b); 1250 } 1251 1252 // Set line style dashed, dotted etc 1253 function SetLineStyle($s) { 1254 if( is_numeric($s) ) { 1255 if( $s<1 || $s>4 ) { 1256 JpGraphError::RaiseL(25101,$s);//(" Illegal numeric argument to SetLineStyle(): ($s)"); 1257 } 1258 } 1259 elseif( is_string($s) ) { 1260 if( $s == "solid" ) $s=1; 1261 elseif( $s == "dotted" ) $s=2; 1262 elseif( $s == "dashed" ) $s=3; 1263 elseif( $s == "longdashed" ) $s=4; 1264 else { 1265 JpGraphError::RaiseL(25102,$s);//(" Illegal string argument to SetLineStyle(): $s"); 1266 } 1267 } 1268 else { 1269 JpGraphError::RaiseL(25103,$s);//(" Illegal argument to SetLineStyle $s"); 1270 } 1271 $old = $this->line_style; 1272 $this->line_style=$s; 1273 return $old; 1274 } 1275 1276 // Same as Line but take the line_style into account 1277 function StyleLine($x1,$y1,$x2,$y2,$aStyle='', $from_grid_class = false) { 1278 if( $this->line_weight <= 0 ) return; 1279 1280 if( $aStyle === '' ) { 1281 $aStyle = $this->line_style; 1282 } 1283 1284 $dashed_line_method = 'DashedLine'; 1285 if ($from_grid_class) { 1286 $dashed_line_method = 'DashedLineForGrid'; 1287 } 1288 1289 // Add error check since dashed line will only work if anti-alias is disabled 1290 // this is a limitation in GD 1291 1292 if( $aStyle == 1 ) { 1293 // Solid style. We can handle anti-aliasing for this 1294 $this->Line($x1,$y1,$x2,$y2); 1295 } 1296 else { 1297 // Since the GD routines doesn't handle AA for styled line 1298 // we have no option than to turn it off to get any lines at 1299 // all if the weight > 1 1300 $oldaa = $this->GetAntiAliasing(); 1301 if( $oldaa && $this->line_weight > 1 ) { 1302 $this->SetAntiAliasing(false); 1303 } 1304 1305 switch( $aStyle ) { 1306 case 2: // Dotted 1307 $this->$dashed_line_method($x1,$y1,$x2,$y2,2,6); 1308 break; 1309 case 3: // Dashed 1310 $this->$dashed_line_method($x1,$y1,$x2,$y2,5,9); 1311 break; 1312 case 4: // Longdashes 1313 $this->$dashed_line_method($x1,$y1,$x2,$y2,9,13); 1314 break; 1315 default: 1316 JpGraphError::RaiseL(25104,$this->line_style);//(" Unknown line style: $this->line_style "); 1317 break; 1318 } 1319 if( $oldaa ) { 1320 $this->SetAntiAliasing(true); 1321 } 1322 } 1323 } 1324 1325 function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) { 1326 1327 if( $this->line_weight <= 0 ) return; 1328 1329 // Add error check to make sure anti-alias is not enabled. 1330 // Dashed line does not work with anti-alias enabled. This 1331 // is a limitation in GD. 1332 if( $this->use_anti_aliasing ) { 1333// JpGraphError::RaiseL(25129); // Anti-alias can not be used with dashed lines. Please disable anti-alias or use solid lines. 1334 } 1335 1336 $x1 = round($x1); 1337 $x2 = round($x2); 1338 $y1 = round($y1); 1339 $y2 = round($y2); 1340 1341 $dash_length *= SUPERSAMPLING_SCALE; 1342 $dash_space *= SUPERSAMPLING_SCALE; 1343 1344 $style = array_fill(0,$dash_length,$this->current_color); 1345 $style = array_pad($style,$dash_space,IMG_COLOR_TRANSPARENT); 1346 imagesetstyle($this->img, $style); 1347 imageline($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); 1348 1349 $this->lastx = $x2; 1350 $this->lasty = $y2; 1351 } 1352 1353 function DashedLineForGrid($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) { 1354 1355 if( $this->line_weight <= 0 ) return; 1356 1357 // Add error check to make sure anti-alias is not enabled. 1358 // Dashed line does not work with anti-alias enabled. This 1359 // is a limitation in GD. 1360 if( $this->use_anti_aliasing ) { 1361// JpGraphError::RaiseL(25129); // Anti-alias can not be used with dashed lines. Please disable anti-alias or use solid lines. 1362 } 1363 1364 $x1 = round($x1); 1365 $x2 = round($x2); 1366 $y1 = round($y1); 1367 $y2 = round($y2); 1368 1369 /* 1370 $dash_length *= $this->scale; 1371 $dash_space *= $this->scale; 1372 */ 1373 1374 $dash_length = 2; 1375 $dash_length = 4; 1376 imagesetthickness($this->img, 1); 1377 $style = array_fill(0,$dash_length, $this->current_color); //hexdec('CCCCCC')); 1378 $style = array_pad($style,$dash_space,IMG_COLOR_TRANSPARENT); 1379 imagesetstyle($this->img, $style); 1380 imageline($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); 1381 1382 $this->lastx = $x2; 1383 $this->lasty = $y2; 1384 } 1385 1386 function Line($x1,$y1,$x2,$y2) { 1387 1388 if( $this->line_weight <= 0 ) return; 1389 1390 $x1 = round($x1); 1391 $x2 = round($x2); 1392 $y1 = round($y1); 1393 $y2 = round($y2); 1394 1395 imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color); 1396// $this->DrawLine($this->img, $x1, $y1, $x2, $y2, $this->line_weight, $this->current_color); 1397 $this->lastx=$x2; 1398 $this->lasty=$y2; 1399 } 1400 1401 function Polygon($p,$closed=FALSE,$fast=FALSE) { 1402 1403 if( $this->line_weight <= 0 ) return; 1404 1405 $n=count($p); 1406 $oldx = $p[0]; 1407 $oldy = $p[1]; 1408 if( $fast ) { 1409 for( $i=2; $i < $n; $i+=2 ) { 1410 imageline($this->img,$oldx,$oldy,$p[$i],$p[$i+1],$this->current_color); 1411 $oldx = $p[$i]; 1412 $oldy = $p[$i+1]; 1413 } 1414 if( $closed ) { 1415 imageline($this->img,$p[$n*2-2],$p[$n*2-1],$p[0],$p[1],$this->current_color); 1416 } 1417 } 1418 else { 1419 for( $i=2; $i < $n; $i+=2 ) { 1420 $this->StyleLine($oldx,$oldy,$p[$i],$p[$i+1]); 1421 $oldx = $p[$i]; 1422 $oldy = $p[$i+1]; 1423 } 1424 if( $closed ) { 1425 $this->StyleLine($oldx,$oldy,$p[0],$p[1]); 1426 } 1427 } 1428 } 1429 1430 function FilledPolygon($pts) { 1431 $n=count($pts); 1432 if( $n == 0 ) { 1433 JpGraphError::RaiseL(25105);//('NULL data specified for a filled polygon. Check that your data is not NULL.'); 1434 } 1435 for($i=0; $i < $n; ++$i) { 1436 $pts[$i] = round($pts[$i]); 1437 } 1438 $old = $this->line_weight; 1439 imagesetthickness($this->img,1); 1440 imagefilledpolygon($this->img,$pts,count($pts)/2,$this->current_color); 1441 $this->line_weight = $old; 1442 imagesetthickness($this->img,$old); 1443 } 1444 1445 function Rectangle($xl,$yu,$xr,$yl) { 1446 $this->Polygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl,$xl,$yu)); 1447 } 1448 1449 function FilledRectangle($xl,$yu,$xr,$yl) { 1450 $this->FilledPolygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl)); 1451 } 1452 1453 function FilledRectangle2($xl,$yu,$xr,$yl,$color1,$color2,$style=1) { 1454 // Fill a rectangle with lines of two colors 1455 if( $style===1 ) { 1456 // Horizontal stripe 1457 if( $yl < $yu ) { 1458 $t = $yl; $yl=$yu; $yu=$t; 1459 } 1460 for( $y=$yu; $y <= $yl; ++$y) { 1461 $this->SetColor($color1); 1462 $this->Line($xl,$y,$xr,$y); 1463 ++$y; 1464 $this->SetColor($color2); 1465 $this->Line($xl,$y,$xr,$y); 1466 } 1467 } 1468 else { 1469 if( $xl < $xl ) { 1470 $t = $xl; $xl=$xr; $xr=$t; 1471 } 1472 for( $x=$xl; $x <= $xr; ++$x) { 1473 $this->SetColor($color1); 1474 $this->Line($x,$yu,$x,$yl); 1475 ++$x; 1476 $this->SetColor($color2); 1477 $this->Line($x,$yu,$x,$yl); 1478 } 1479 } 1480 } 1481 1482 function ShadowRectangle($xl,$yu,$xr,$yl,$fcolor=false,$shadow_width=4,$shadow_color='darkgray',$useAlpha=true) { 1483 // This is complicated by the fact that we must also handle the case where 1484 // the reactangle has no fill color 1485 $xl = floor($xl); 1486 $yu = floor($yu); 1487 $xr = floor($xr); 1488 $yl = floor($yl); 1489 $this->PushColor($shadow_color); 1490 $shadowAlpha=0; 1491 $this->SetLineWeight(1); 1492 $this->SetLineStyle('solid'); 1493 $basecolor = $this->rgb->Color($shadow_color); 1494 $shadow_color = array($basecolor[0],$basecolor[1],$basecolor[2],); 1495 for( $i=0; $i < $shadow_width; ++$i ) { 1496 $this->SetColor($shadow_color,$shadowAlpha); 1497 $this->Line($xr-$shadow_width+$i, $yu+$shadow_width, 1498 $xr-$shadow_width+$i, $yl-$shadow_width-1+$i); 1499 $this->Line($xl+$shadow_width, $yl-$shadow_width+$i, 1500 $xr-$shadow_width+$i, $yl-$shadow_width+$i); 1501 if( $useAlpha ) $shadowAlpha += 1.0/$shadow_width; 1502 } 1503 1504 $this->PopColor(); 1505 if( $fcolor==false ) { 1506 $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1); 1507 } 1508 else { 1509 $this->PushColor($fcolor); 1510 $this->FilledRectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1); 1511 $this->PopColor(); 1512 $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1); 1513 } 1514 } 1515 1516 function FilledRoundedRectangle($xt,$yt,$xr,$yl,$r=5) { 1517 if( $r==0 ) { 1518 $this->FilledRectangle($xt,$yt,$xr,$yl); 1519 return; 1520 } 1521 1522 // To avoid overlapping fillings (which will look strange 1523 // when alphablending is enabled) we have no choice but 1524 // to fill the five distinct areas one by one. 1525 1526 // Center square 1527 $this->FilledRectangle($xt+$r,$yt+$r,$xr-$r,$yl-$r); 1528 // Top band 1529 $this->FilledRectangle($xt+$r,$yt,$xr-$r,$yt+$r); 1530 // Bottom band 1531 $this->FilledRectangle($xt+$r,$yl-$r,$xr-$r,$yl); 1532 // Left band 1533 $this->FilledRectangle($xt,$yt+$r,$xt+$r,$yl-$r); 1534 // Right band 1535 $this->FilledRectangle($xr-$r,$yt+$r,$xr,$yl-$r); 1536 1537 // Topleft & Topright arc 1538 $this->FilledArc($xt+$r,$yt+$r,$r*2,$r*2,180,270); 1539 $this->FilledArc($xr-$r,$yt+$r,$r*2,$r*2,270,360); 1540 1541 // Bottomleft & Bottom right arc 1542 $this->FilledArc($xt+$r,$yl-$r,$r*2,$r*2,90,180); 1543 $this->FilledArc($xr-$r,$yl-$r,$r*2,$r*2,0,90); 1544 1545 } 1546 1547 function RoundedRectangle($xt,$yt,$xr,$yl,$r=5) { 1548 1549 if( $r==0 ) { 1550 $this->Rectangle($xt,$yt,$xr,$yl); 1551 return; 1552 } 1553 1554 // Top & Bottom line 1555 $this->Line($xt+$r,$yt,$xr-$r,$yt); 1556 $this->Line($xt+$r,$yl,$xr-$r,$yl); 1557 1558 // Left & Right line 1559 $this->Line($xt,$yt+$r,$xt,$yl-$r); 1560 $this->Line($xr,$yt+$r,$xr,$yl-$r); 1561 1562 // Topleft & Topright arc 1563 $this->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270); 1564 $this->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360); 1565 1566 // Bottomleft & Bottomright arc 1567 $this->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180); 1568 $this->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90); 1569 } 1570 1571 function FilledBevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='darkgray@0.4') { 1572 $this->FilledRectangle($x1,$y1,$x2,$y2); 1573 $this->Bevel($x1,$y1,$x2,$y2,$depth,$color1,$color2); 1574 } 1575 1576 function Bevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='black@0.5') { 1577 $this->PushColor($color1); 1578 for( $i=0; $i < $depth; ++$i ) { 1579 $this->Line($x1+$i,$y1+$i,$x1+$i,$y2-$i); 1580 $this->Line($x1+$i,$y1+$i,$x2-$i,$y1+$i); 1581 } 1582 $this->PopColor(); 1583 1584 $this->PushColor($color2); 1585 for( $i=0; $i < $depth; ++$i ) { 1586 $this->Line($x1+$i,$y2-$i,$x2-$i,$y2-$i); 1587 $this->Line($x2-$i,$y1+$i,$x2-$i,$y2-$i-1); 1588 } 1589 $this->PopColor(); 1590 } 1591 1592 function StyleLineTo($x,$y) { 1593 $this->StyleLine($this->lastx,$this->lasty,$x,$y); 1594 $this->lastx=$x; 1595 $this->lasty=$y; 1596 } 1597 1598 function LineTo($x,$y) { 1599 $this->Line($this->lastx,$this->lasty,$x,$y); 1600 $this->lastx=$x; 1601 $this->lasty=$y; 1602 } 1603 1604 function Point($x,$y) { 1605 imagesetpixel($this->img,round($x),round($y),$this->current_color); 1606 } 1607 1608 function Fill($x,$y) { 1609 imagefill($this->img,round($x),round($y),$this->current_color); 1610 } 1611 1612 function FillToBorder($x,$y,$aBordColor) { 1613 $bc = $this->rgb->allocate($aBordColor); 1614 if( $bc == -1 ) { 1615 JpGraphError::RaiseL(25106);//('Image::FillToBorder : Can not allocate more colors'); 1616 } 1617 imagefilltoborder($this->img,round($x),round($y),$bc,$this->current_color); 1618 } 1619 1620 function SetExpired($aFlg=true) { 1621 $this->expired = $aFlg; 1622 } 1623 1624 // Generate image header 1625 function Headers() { 1626 1627 // In case we are running from the command line with the client version of 1628 // PHP we can't send any headers. 1629 $sapi = php_sapi_name(); 1630 if( $sapi == 'cli' ) return; 1631 1632 // These parameters are set by headers_sent() but they might cause 1633 // an undefined variable error unless they are initilized 1634 $file=''; 1635 $lineno=''; 1636 if( headers_sent($file,$lineno) ) { 1637 $file=basename($file); 1638 $t = new ErrMsgText(); 1639 $msg = $t->Get(10,$file,$lineno); 1640 die($msg); 1641 } 1642 1643 if ($this->expired) { 1644 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 1645 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); 1646 header("Cache-Control: no-cache, must-revalidate"); 1647 header("Pragma: no-cache"); 1648 } 1649 header("Content-type: image/$this->img_format"); 1650 } 1651 1652 // Adjust image quality for formats that allow this 1653 function SetQuality($q) { 1654 $this->quality = $q; 1655 } 1656 1657 // Stream image to browser or to file 1658 function Stream($aFile=NULL) { 1659 $this->DoSupersampling(); 1660 1661 $func="image".$this->img_format; 1662 if( $this->img_format=="jpeg" && $this->quality != null ) { 1663 $res = @$func($this->img,$aFile,$this->quality); 1664 1665 if(!$res){ 1666 if($aFile != NULL){ 1667 JpGraphError::RaiseL(25107,$aFile);//("Can't write to file '$aFile'. Check that the process running PHP has enough permission."); 1668 }else{ 1669 JpGraphError::RaiseL(25108);//("Can't stream image. This is most likely due to a faulty PHP/GD setup. Try to recompile PHP and use the built-in GD library that comes with PHP."); 1670 } 1671 1672 } 1673 } 1674 else { 1675 if( $aFile != NULL ) { 1676 $res = @$func($this->img,$aFile); 1677 if( !$res ) { 1678 JpGraphError::RaiseL(25107,$aFile);//("Can't write to file '$aFile'. Check that the process running PHP has enough permission."); 1679 } 1680 } 1681 else { 1682 $res = @$func($this->img); 1683 if( !$res ) { 1684 JpGraphError::RaiseL(25108);//("Can't stream image. This is most likely due to a faulty PHP/GD setup. Try to recompile PHP and use the built-in GD library that comes with PHP."); 1685 } 1686 1687 } 1688 } 1689 } 1690 1691 // Do SuperSampling using $scale 1692 function DoSupersampling() { 1693 if (SUPERSAMPLING_SCALE <= 1) { 1694 return $this->img; 1695 } 1696 1697 $dst_img = @imagecreatetruecolor($this->original_width, $this->original_height); 1698 imagecopyresampled($dst_img, $this->img, 0, 0, 0, 0, $this->original_width, $this->original_height, $this->width, $this->height); 1699 $this->Destroy(); 1700 return $this->img = $dst_img; 1701 } 1702 1703 // Clear resources used by image (this is normally not used since all resources are/should be 1704 // returned when the script terminates 1705 function Destroy() { 1706 imagedestroy($this->img); 1707 } 1708 1709 // Specify image format. Note depending on your installation 1710 // of PHP not all formats may be supported. 1711 function SetImgFormat($aFormat,$aQuality=75) { 1712 $this->quality = $aQuality; 1713 $aFormat = strtolower($aFormat); 1714 $tst = true; 1715 $supported = imagetypes(); 1716 if( $aFormat=="auto" ) { 1717 if( $supported & IMG_PNG ) $this->img_format="png"; 1718 elseif( $supported & IMG_JPG ) $this->img_format="jpeg"; 1719 elseif( $supported & IMG_GIF ) $this->img_format="gif"; 1720 elseif( $supported & IMG_WBMP ) $this->img_format="wbmp"; 1721 elseif( $supported & IMG_XPM ) $this->img_format="xpm"; 1722 else { 1723 JpGraphError::RaiseL(25109);//("Your PHP (and GD-lib) installation does not appear to support any known graphic formats. You need to first make sure GD is compiled as a module to PHP. If you also want to use JPEG images you must get the JPEG library. Please see the PHP docs for details."); 1724 } 1725 return true; 1726 } 1727 else { 1728 if( $aFormat=="jpeg" || $aFormat=="png" || $aFormat=="gif" ) { 1729 if( $aFormat=="jpeg" && !($supported & IMG_JPG) ) $tst=false; 1730 elseif( $aFormat=="png" && !($supported & IMG_PNG) ) $tst=false; 1731 elseif( $aFormat=="gif" && !($supported & IMG_GIF) ) $tst=false; 1732 elseif( $aFormat=="wbmp" && !($supported & IMG_WBMP) ) $tst=false; 1733 elseif( $aFormat=="xpm" && !($supported & IMG_XPM) ) $tst=false; 1734 else { 1735 $this->img_format=$aFormat; 1736 return true; 1737 } 1738 } 1739 else { 1740 $tst=false; 1741 } 1742 if( !$tst ) { 1743 JpGraphError::RaiseL(25110,$aFormat);//(" Your PHP installation does not support the chosen graphic format: $aFormat"); 1744 } 1745 } 1746 } 1747 1748 /** 1749 * Draw Line 1750 */ 1751 function DrawLine($im, $x1, $y1, $x2, $y2, $weight, $color) { 1752 if ($weight == 1) { 1753 return imageline($im,$x1,$y1,$x2,$y2,$color); 1754 } 1755 1756 $angle=(atan2(($y1 - $y2), ($x2 - $x1))); 1757 1758 $dist_x = $weight * (sin($angle)) / 2; 1759 $dist_y = $weight * (cos($angle)) / 2; 1760 1761 $p1x=ceil(($x1 + $dist_x)); 1762 $p1y=ceil(($y1 + $dist_y)); 1763 $p2x=ceil(($x2 + $dist_x)); 1764 $p2y=ceil(($y2 + $dist_y)); 1765 $p3x=ceil(($x2 - $dist_x)); 1766 $p3y=ceil(($y2 - $dist_y)); 1767 $p4x=ceil(($x1 - $dist_x)); 1768 $p4y=ceil(($y1 - $dist_y)); 1769 1770 $array=array($p1x,$p1y,$p2x,$p2y,$p3x,$p3y,$p4x,$p4y); 1771 imagefilledpolygon ( $im, $array, (count($array)/2), $color ); 1772 1773 // for antialias 1774 imageline($im, $p1x, $p1y, $p2x, $p2y, $color); 1775 imageline($im, $p3x, $p3y, $p4x, $p4y, $color); 1776 return; 1777 1778 1779 1780 return imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color); 1781 $weight = 8; 1782 if ($weight <= 1) { 1783 return imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color); 1784 } 1785 1786 $pts = array(); 1787 1788 $weight /= 2; 1789 1790 if ($y2 - $y1 == 0) { 1791 // x line 1792 $pts = array(); 1793 $pts[] = $x1; $pts[] = $y1 - $weight; 1794 $pts[] = $x1; $pts[] = $y1 + $weight; 1795 $pts[] = $x2; $pts[] = $y2 + $weight; 1796 $pts[] = $x2; $pts[] = $y2 - $weight; 1797 1798 } elseif ($x2 - $x1 == 0) { 1799 // y line 1800 $pts = array(); 1801 $pts[] = $x1 - $weight; $pts[] = $y1; 1802 $pts[] = $x1 + $weight; $pts[] = $y1; 1803 $pts[] = $x2 + $weight; $pts[] = $y2; 1804 $pts[] = $x2 - $weight; $pts[] = $y2; 1805 1806 } else { 1807 1808 var_dump($x1, $x2, $y1, $y2); 1809 $length = sqrt(pow($x2 - $x1, 2) + pow($y2 - $y1, 2)); 1810 var_dump($length);exit; 1811 exit; 1812 1813/* 1814 $lean = ($y2 - $y1) / ($x2 - $x1); 1815 $lean2 = -1 / $lean; 1816 $sin = $lean / ($y2 - $y1); 1817 $cos = $lean / ($x2 - $x1); 1818 1819 $pts[] = $x1 + (-$weight * $sin); $pts[] = $y1 + (-$weight * $cos); 1820 $pts[] = $x1 + (+$weight * $sin); $pts[] = $y1 + (+$weight * $cos); 1821 $pts[] = $x2 + (+$weight * $sin); $pts[] = $y2 + (+$weight * $cos); 1822 $pts[] = $x2 + (-$weight * $sin); $pts[] = $y2 + (-$weight * $cos); 1823*/ 1824 } 1825 1826//print_r($pts);exit; 1827 if (count($pts)/2 < 3) { 1828 return; 1829 } 1830 1831 imagesetthickness($im, 1); 1832 imagefilledpolygon($im, $pts,count($pts)/2, $color); 1833 1834 1835 $weight *= 2; 1836 1837// $this->DrawImageSmoothArc($im, $x1, $y1, $weight, $weight, 0, 360, $color); 1838// $this->DrawImageSmoothArc($im, $x2, $y2, $weight, $weight, 0, 360, $color); 1839 } 1840 1841 1842 function DrawImageSmoothArc($im, $xc, $yc, $w, $h, $s, $e, $color, $style = null) { 1843 $tmp = $s; 1844 $s = (360 - $e) / 180 * M_PI; 1845 $e = (360 - $tmp) / 180 * M_PI; 1846 return imageSmoothArc($im, round($xc), round($yc), round($w), round($h), $this->CreateColorForImageSmoothArc($color), $s, $e); 1847 } 1848 1849 function CreateColorForImageSmoothArc($color) { 1850 $alpha = $color >> 24 & 0xFF; 1851 $red = $color >> 16 & 0xFF; 1852 $green = $color >> 8 & 0xFF; 1853 $blue = $color & 0xFF; 1854 1855//var_dump($alpha, $red, $green, $blue);exit; 1856 1857 return array($red, $green, $blue, $alpha); 1858 } 1859 1860 function imageSmoothCircle( &$img, $cx, $cy, $cr, $color ) { 1861 $ir = $cr; 1862 $ix = 0; 1863 $iy = $ir; 1864 $ig = 2 * $ir - 3; 1865 $idgr = -6; 1866 $idgd = 4 * $ir - 10; 1867 $fill = imageColorExactAlpha( $img, $color[ 'R' ], $color[ 'G' ], $color[ 'B' ], 0 ); 1868 imageLine( $img, $cx + $cr - 1, $cy, $cx, $cy, $fill ); 1869 imageLine( $img, $cx - $cr + 1, $cy, $cx - 1, $cy, $fill ); 1870 imageLine( $img, $cx, $cy + $cr - 1, $cx, $cy + 1, $fill ); 1871 imageLine( $img, $cx, $cy - $cr + 1, $cx, $cy - 1, $fill ); 1872 $draw = imageColorExactAlpha( $img, $color[ 'R' ], $color[ 'G' ], $color[ 'B' ], 42 ); 1873 imageSetPixel( $img, $cx + $cr, $cy, $draw ); 1874 imageSetPixel( $img, $cx - $cr, $cy, $draw ); 1875 imageSetPixel( $img, $cx, $cy + $cr, $draw ); 1876 imageSetPixel( $img, $cx, $cy - $cr, $draw ); 1877 while ( $ix <= $iy - 2 ) { 1878 if ( $ig < 0 ) { 1879 $ig += $idgd; 1880 $idgd -= 8; 1881 $iy--; 1882 } else { 1883 $ig += $idgr; 1884 $idgd -= 4; 1885 } 1886 $idgr -= 4; 1887 $ix++; 1888 imageLine( $img, $cx + $ix, $cy + $iy - 1, $cx + $ix, $cy + $ix, $fill ); 1889 imageLine( $img, $cx + $ix, $cy - $iy + 1, $cx + $ix, $cy - $ix, $fill ); 1890 imageLine( $img, $cx - $ix, $cy + $iy - 1, $cx - $ix, $cy + $ix, $fill ); 1891 imageLine( $img, $cx - $ix, $cy - $iy + 1, $cx - $ix, $cy - $ix, $fill ); 1892 imageLine( $img, $cx + $iy - 1, $cy + $ix, $cx + $ix, $cy + $ix, $fill ); 1893 imageLine( $img, $cx + $iy - 1, $cy - $ix, $cx + $ix, $cy - $ix, $fill ); 1894 imageLine( $img, $cx - $iy + 1, $cy + $ix, $cx - $ix, $cy + $ix, $fill ); 1895 imageLine( $img, $cx - $iy + 1, $cy - $ix, $cx - $ix, $cy - $ix, $fill ); 1896 $filled = 0; 1897 for ( $xx = $ix - 0.45; $xx < $ix + 0.5; $xx += 0.2 ) { 1898 for ( $yy = $iy - 0.45; $yy < $iy + 0.5; $yy += 0.2 ) { 1899 if ( sqrt( pow( $xx, 2 ) + pow( $yy, 2 ) ) < $cr ) $filled += 4; 1900 } 1901 } 1902 $draw = imageColorExactAlpha( $img, $color[ 'R' ], $color[ 'G' ], $color[ 'B' ], ( 100 - $filled ) ); 1903 imageSetPixel( $img, $cx + $ix, $cy + $iy, $draw ); 1904 imageSetPixel( $img, $cx + $ix, $cy - $iy, $draw ); 1905 imageSetPixel( $img, $cx - $ix, $cy + $iy, $draw ); 1906 imageSetPixel( $img, $cx - $ix, $cy - $iy, $draw ); 1907 imageSetPixel( $img, $cx + $iy, $cy + $ix, $draw ); 1908 imageSetPixel( $img, $cx + $iy, $cy - $ix, $draw ); 1909 imageSetPixel( $img, $cx - $iy, $cy + $ix, $draw ); 1910 imageSetPixel( $img, $cx - $iy, $cy - $ix, $draw ); 1911 } 1912 } 1913 1914 function __get($name) { 1915 1916 if (strpos($name, 'raw_') !== false) { 1917 // if $name == 'raw_left_margin' , return $this->_left_margin; 1918 $variable_name = '_' . str_replace('raw_', '', $name); 1919 return $this->$variable_name; 1920 } 1921 1922 $variable_name = '_' . $name; 1923 1924 if (isset($this->$variable_name)) { 1925 return $this->$variable_name * SUPERSAMPLING_SCALE; 1926 } else { 1927 JpGraphError::RaiseL('25132', $name); 1928 } 1929 } 1930 1931 function __set($name, $value) { 1932 $this->{'_'.$name} = $value; 1933 } 1934 1935} // CLASS 1936 1937//=================================================== 1938// CLASS RotImage 1939// Description: Exactly as Image but draws the image at 1940// a specified angle around a specified rotation point. 1941//=================================================== 1942class RotImage extends Image { 1943 public $a=0; 1944 public $dx=0,$dy=0,$transx=0,$transy=0; 1945 private $m=array(); 1946 1947 function __construct($aWidth,$aHeight,$a=0,$aFormat=DEFAULT_GFORMAT,$aSetAutoMargin=true) { 1948 parent::__construct($aWidth,$aHeight,$aFormat,$aSetAutoMargin); 1949 $this->dx=$this->width/2; 1950 $this->dy=$this->height/2; 1951 $this->SetAngle($a); 1952 } 1953 1954 function SetCenter($dx,$dy) { 1955 $old_dx = $this->dx; 1956 $old_dy = $this->dy; 1957 $this->dx=$dx; 1958 $this->dy=$dy; 1959 $this->SetAngle($this->a); 1960 return array($old_dx,$old_dy); 1961 } 1962 1963 function SetTranslation($dx,$dy) { 1964 $old = array($this->transx,$this->transy); 1965 $this->transx = $dx; 1966 $this->transy = $dy; 1967 return $old; 1968 } 1969 1970 function UpdateRotMatrice() { 1971 $a = $this->a; 1972 $a *= M_PI/180; 1973 $sa=sin($a); $ca=cos($a); 1974 // Create the rotation matrix 1975 $this->m[0][0] = $ca; 1976 $this->m[0][1] = -$sa; 1977 $this->m[0][2] = $this->dx*(1-$ca) + $sa*$this->dy ; 1978 $this->m[1][0] = $sa; 1979 $this->m[1][1] = $ca; 1980 $this->m[1][2] = $this->dy*(1-$ca) - $sa*$this->dx ; 1981 } 1982 1983 function SetAngle($a) { 1984 $tmp = $this->a; 1985 $this->a = $a; 1986 $this->UpdateRotMatrice(); 1987 return $tmp; 1988 } 1989 1990 function Circle($xc,$yc,$r) { 1991 list($xc,$yc) = $this->Rotate($xc,$yc); 1992 parent::Circle($xc,$yc,$r); 1993 } 1994 1995 function FilledCircle($xc,$yc,$r) { 1996 list($xc,$yc) = $this->Rotate($xc,$yc); 1997 parent::FilledCircle($xc,$yc,$r); 1998 } 1999 2000 2001 function Arc($xc,$yc,$w,$h,$s,$e) { 2002 list($xc,$yc) = $this->Rotate($xc,$yc); 2003 $s += $this->a; 2004 $e += $this->a; 2005 parent::Arc($xc,$yc,$w,$h,$s,$e); 2006 } 2007 2008 function FilledArc($xc,$yc,$w,$h,$s,$e,$style='') { 2009 list($xc,$yc) = $this->Rotate($xc,$yc); 2010 $s += $this->a; 2011 $e += $this->a; 2012 parent::FilledArc($xc,$yc,$w,$h,$s,$e); 2013 } 2014 2015 function SetMargin($lm,$rm,$tm,$bm) { 2016 parent::SetMargin($lm,$rm,$tm,$bm); 2017 $this->UpdateRotMatrice(); 2018 } 2019 2020 function Rotate($x,$y) { 2021 // Optimization. Ignore rotation if Angle==0 || Angle==360 2022 if( $this->a == 0 || $this->a == 360 ) { 2023 return array($x + $this->transx, $y + $this->transy ); 2024 } 2025 else { 2026 $x1=round($this->m[0][0]*$x + $this->m[0][1]*$y,1) + $this->m[0][2] + $this->transx; 2027 $y1=round($this->m[1][0]*$x + $this->m[1][1]*$y,1) + $this->m[1][2] + $this->transy; 2028 return array($x1,$y1); 2029 } 2030 } 2031 2032 function CopyMerge($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1,$aMix=100) { 2033 list($toX,$toY) = $this->Rotate($toX,$toY); 2034 parent::CopyMerge($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight,$aMix); 2035 2036 } 2037 2038 function ArrRotate($pnts) { 2039 $n = count($pnts)-1; 2040 for($i=0; $i < $n; $i+=2) { 2041 list ($x,$y) = $this->Rotate($pnts[$i],$pnts[$i+1]); 2042 $pnts[$i] = $x; $pnts[$i+1] = $y; 2043 } 2044 return $pnts; 2045 } 2046 2047 function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) { 2048 list($x1,$y1) = $this->Rotate($x1,$y1); 2049 list($x2,$y2) = $this->Rotate($x2,$y2); 2050 parent::DashedLine($x1,$y1,$x2,$y2,$dash_length,$dash_space); 2051 } 2052 2053 function Line($x1,$y1,$x2,$y2) { 2054 list($x1,$y1) = $this->Rotate($x1,$y1); 2055 list($x2,$y2) = $this->Rotate($x2,$y2); 2056 parent::Line($x1,$y1,$x2,$y2); 2057 } 2058 2059 function Rectangle($x1,$y1,$x2,$y2) { 2060 // Rectangle uses Line() so it will be rotated through that call 2061 parent::Rectangle($x1,$y1,$x2,$y2); 2062 } 2063 2064 function FilledRectangle($x1,$y1,$x2,$y2) { 2065 if( $y1==$y2 || $x1==$x2 ) 2066 $this->Line($x1,$y1,$x2,$y2); 2067 else 2068 $this->FilledPolygon(array($x1,$y1,$x2,$y1,$x2,$y2,$x1,$y2)); 2069 } 2070 2071 function Polygon($pnts,$closed=FALSE,$fast=FALSE) { 2072 // Polygon uses Line() so it will be rotated through that call unless 2073 // fast drawing routines are used in which case a rotate is needed 2074 if( $fast ) { 2075 parent::Polygon($this->ArrRotate($pnts)); 2076 } 2077 else { 2078 parent::Polygon($pnts,$closed,$fast); 2079 } 2080 } 2081 2082 function FilledPolygon($pnts) { 2083 parent::FilledPolygon($this->ArrRotate($pnts)); 2084 } 2085 2086 function Point($x,$y) { 2087 list($xp,$yp) = $this->Rotate($x,$y); 2088 parent::Point($xp,$yp); 2089 } 2090 2091 function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) { 2092 list($xp,$yp) = $this->Rotate($x,$y); 2093 return parent::StrokeText($xp,$yp,$txt,$dir,$paragraph_align,$debug); 2094 } 2095} 2096 2097//======================================================================= 2098// CLASS ImgStreamCache 2099// Description: Handle caching of graphs to files. All image output goes 2100// through this class 2101//======================================================================= 2102class ImgStreamCache { 2103 private $cache_dir, $timeout=0; // Infinite timeout 2104 //--------------- 2105 // CONSTRUCTOR 2106 function __construct($aCacheDir=CACHE_DIR) { 2107 $this->cache_dir = $aCacheDir; 2108 } 2109 2110 //--------------- 2111 // PUBLIC METHODS 2112 2113 // Specify a timeout (in minutes) for the file. If the file is older then the 2114 // timeout value it will be overwritten with a newer version. 2115 // If timeout is set to 0 this is the same as infinite large timeout and if 2116 // timeout is set to -1 this is the same as infinite small timeout 2117 function SetTimeout($aTimeout) { 2118 $this->timeout=$aTimeout; 2119 } 2120 2121 // Output image to browser and also write it to the cache 2122 function PutAndStream($aImage,$aCacheFileName,$aInline,$aStrokeFileName) { 2123 2124 // Check if we should always stroke the image to a file 2125 if( _FORCE_IMGTOFILE ) { 2126 $aStrokeFileName = _FORCE_IMGDIR.GenImgName(); 2127 } 2128 2129 if( $aStrokeFileName != '' ) { 2130 2131 if( $aStrokeFileName == 'auto' ) { 2132 $aStrokeFileName = GenImgName(); 2133 } 2134 2135 if( file_exists($aStrokeFileName) ) { 2136 2137 // Wait for lock (to make sure no readers are trying to access the image) 2138 $fd = fopen($aStrokeFileName,'w'); 2139 $lock = flock($fd, LOCK_EX); 2140 2141 // Since the image write routines only accepts a filename which must not 2142 // exist we need to delete the old file first 2143 if( !@unlink($aStrokeFileName) ) { 2144 $lock = flock($fd, LOCK_UN); 2145 JpGraphError::RaiseL(25111,$aStrokeFileName); 2146 //(" Can't delete cached image $aStrokeFileName. Permission problem?"); 2147 } 2148 $aImage->Stream($aStrokeFileName); 2149 $lock = flock($fd, LOCK_UN); 2150 fclose($fd); 2151 2152 } 2153 else { 2154 $aImage->Stream($aStrokeFileName); 2155 } 2156 2157 return; 2158 } 2159 2160 if( $aCacheFileName != '' && USE_CACHE) { 2161 2162 $aCacheFileName = $this->cache_dir . $aCacheFileName; 2163 if( file_exists($aCacheFileName) ) { 2164 if( !$aInline ) { 2165 // If we are generating image off-line (just writing to the cache) 2166 // and the file exists and is still valid (no timeout) 2167 // then do nothing, just return. 2168 $diff=time()-filemtime($aCacheFileName); 2169 if( $diff < 0 ) { 2170 JpGraphError::RaiseL(25112,$aCacheFileName); 2171 //(" Cached imagefile ($aCacheFileName) has file date in the future!!"); 2172 } 2173 if( $this->timeout>0 && ($diff <= $this->timeout*60) ) return; 2174 } 2175 2176 // Wait for lock (to make sure no readers are trying to access the image) 2177 $fd = fopen($aCacheFileName,'w'); 2178 $lock = flock($fd, LOCK_EX); 2179 2180 if( !@unlink($aCacheFileName) ) { 2181 $lock = flock($fd, LOCK_UN); 2182 JpGraphError::RaiseL(25113,$aStrokeFileName); 2183 //(" Can't delete cached image $aStrokeFileName. Permission problem?"); 2184 } 2185 $aImage->Stream($aCacheFileName); 2186 $lock = flock($fd, LOCK_UN); 2187 fclose($fd); 2188 2189 } 2190 else { 2191 $this->MakeDirs(dirname($aCacheFileName)); 2192 if( !is_writeable(dirname($aCacheFileName)) ) { 2193 JpGraphError::RaiseL(25114,$aCacheFileName); 2194 //('PHP has not enough permissions to write to the cache file '.$aCacheFileName.'. Please make sure that the user running PHP has write permission for this file if you wan to use the cache system with JpGraph.'); 2195 } 2196 $aImage->Stream($aCacheFileName); 2197 } 2198 2199 $res=true; 2200 // Set group to specified 2201 if( CACHE_FILE_GROUP != '' ) { 2202 $res = @chgrp($aCacheFileName,CACHE_FILE_GROUP); 2203 } 2204 if( CACHE_FILE_MOD != '' ) { 2205 $res = @chmod($aCacheFileName,CACHE_FILE_MOD); 2206 } 2207 if( !$res ) { 2208 JpGraphError::RaiseL(25115,$aStrokeFileName); 2209 //(" Can't set permission for cached image $aStrokeFileName. Permission problem?"); 2210 } 2211 2212 $aImage->Destroy(); 2213 if( $aInline ) { 2214 if ($fh = @fopen($aCacheFileName, "rb") ) { 2215 $aImage->Headers(); 2216 fpassthru($fh); 2217 return; 2218 } 2219 else { 2220 JpGraphError::RaiseL(25116,$aFile);//(" Cant open file from cache [$aFile]"); 2221 } 2222 } 2223 } 2224 elseif( $aInline ) { 2225 $aImage->Headers(); 2226 $aImage->Stream(); 2227 return; 2228 } 2229 } 2230 2231 function IsValid($aCacheFileName) { 2232 $aCacheFileName = $this->cache_dir.$aCacheFileName; 2233 if ( USE_CACHE && file_exists($aCacheFileName) ) { 2234 $diff=time()-filemtime($aCacheFileName); 2235 if( $this->timeout>0 && ($diff > $this->timeout*60) ) { 2236 return false; 2237 } 2238 else { 2239 return true; 2240 } 2241 } 2242 else { 2243 return false; 2244 } 2245 } 2246 2247 function StreamImgFile($aImage,$aCacheFileName) { 2248 $aCacheFileName = $this->cache_dir.$aCacheFileName; 2249 if ( $fh = @fopen($aCacheFileName, 'rb') ) { 2250 $lock = flock($fh, LOCK_SH); 2251 $aImage->Headers(); 2252 fpassthru($fh); 2253 $lock = flock($fh, LOCK_UN); 2254 fclose($fh); 2255 return true; 2256 } 2257 else { 2258 JpGraphError::RaiseL(25117,$aCacheFileName);//(" Can't open cached image \"$aCacheFileName\" for reading."); 2259 } 2260 } 2261 2262 // Check if a given image is in cache and in that case 2263 // pass it directly on to web browser. Return false if the 2264 // image file doesn't exist or exists but is to old 2265 function GetAndStream($aImage,$aCacheFileName) { 2266 if( $this->Isvalid($aCacheFileName) ) { 2267 return $this->StreamImgFile($aImage,$aCacheFileName); 2268 } 2269 else { 2270 return false; 2271 } 2272 } 2273 2274 //--------------- 2275 // PRIVATE METHODS 2276 // Create all necessary directories in a path 2277 function MakeDirs($aFile) { 2278 $dirs = array(); 2279 // In order to better work when open_basedir is enabled 2280 // we do not create directories in the root path 2281 while ( $aFile != '/' && !(file_exists($aFile)) ) { 2282 $dirs[] = $aFile.'/'; 2283 $aFile = dirname($aFile); 2284 } 2285 for ($i = sizeof($dirs)-1; $i>=0; $i--) { 2286 if(! @mkdir($dirs[$i],0777) ) { 2287 JpGraphError::RaiseL(25118,$aFile);//(" Can't create directory $aFile. Make sure PHP has write permission to this directory."); 2288 } 2289 // We also specify mode here after we have changed group. 2290 // This is necessary if Apache user doesn't belong the 2291 // default group and hence can't specify group permission 2292 // in the previous mkdir() call 2293 if( CACHE_FILE_GROUP != "" ) { 2294 $res=true; 2295 $res =@chgrp($dirs[$i],CACHE_FILE_GROUP); 2296 $res = @chmod($dirs[$i],0777); 2297 if( !$res ) { 2298 JpGraphError::RaiseL(25119,$aFile);//(" Can't set permissions for $aFile. Permission problems?"); 2299 } 2300 } 2301 } 2302 return true; 2303 } 2304} // CLASS Cache 2305 2306?> 2307