Subversion Repositories Applications.annuaire

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

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