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