source: trunk/xgraph/jpgraph/gd_image.inc.php

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