source: trunk/client/modules/Elezioni/grafici/gd_image.inc.php@ 2

Last change on this file since 2 was 2, checked in by root, 15 years ago

importo il progetto

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