PHP 8.2 Compatibility Improvement, better implementation.
[fa-stable.git] / reporting / includes / class.graphic.inc
1 <?php
2 /*
3
4 $pg = new Chart('bar'); // Width and Height is set automatically4
5 $pg->setTitle("Testing the Chart");
6 $pg->setLabels(array('Jan', 'Feb', 'Mar'));
7 $pg->addSerie('Sales', array(10345, 15666, 12222), false, true); 
8 $pg->addSerie('Costs', array(20767, 10456, 2888), false, false);
9 $pg->addSerie('Result', array(15657, 7567, 12890), 'spline', false);
10 $pg->setXTitle("Names");
11 $pg->setYTitle("Amount");
12 $pg->setDTitle(number_format("15666")); // only printed on donut or half donut
13 //$filename = "test.png";
14 $pg->display(); // with filename to file
15
16 In your html file you set it up as:
17 .....
18 <img src="example.php" border="1" />
19 .....
20
21 You can supply extra parameters to display(). Ex. $pg->display("test.png") will save the image to a file.
22 Ex. $pg->display("", true) will paint a border around the image. It might be suitable if you choose to save to
23 file for later presentation.
24
25 ///// END OF EXAMPLE.PHP /////
26
27         Here is a list of all types you may set:
28
29         type
30         ----
31         'bar'                   1 => Vertical bars (default)
32         'horizontalBar' 2 => Horizontal bars
33         'dot'                   3 => Dots
34         'line'                  4 => Lines - Series Type Fill => Area
35         'pie'                   5 => Pie
36         'donut'                 6 => Donut
37         'halfDonut'             7 => Half Donut
38         'spline'                8 => Splines - Series Type fill => Area
39         'table'                 9 => Simple HTML table
40
41         labels[0]  =>  Name of the first parameter in Axis X
42         labels[1]  =>  Name of the second parameter in Axis X
43         ... (etc)
44
45         series[0]['data'][0]  =>  Value relative for "labels[0]"
46         series[0]['data'][1]  =>  Value relative for "labels[1]"
47         ... (etc)
48
49         series[1]['data'][0]  =>  Value relative for "labels[0]"
50         series[1]['data'][1]  =>  Value relative for "labels[1]"
51         ... (etc)
52    
53         NOTE: When using 'pie', 'donut' and 'halfDonut' only the first data set is used.
54    
55         That's all! Hope you make a good use of it!
56         It would be nice to receive feedback from others users. All comments are welcome!
57
58         Regards,
59
60         Joe Hunt
61 */
62 define('MAXLEN', 27); // we cut after 25 chars + ...
63 class Chart
64 {
65         var $id;
66         var $labels = array();
67         var $series = array();
68         static $palette = array('#008cc9','#4db625','#ef5500','#eef100','#05c6e6', '#5ee66a'); // Currently in Use      
69         var $color = array();
70         var $num_series = 0;
71         var $title;
72         var $axis_x;
73         var $axis_y;
74         var $type = 1;
75         var $donut_title = "";
76         var $set_values = true;
77         var $angle = 0;
78         var $latin_notation;
79         var $width;
80         var $height;
81         var $title_height;
82         var $size = 12;
83         var $tsize = 18;
84         var $num_labels;
85         var $sum_total = array();
86         var $higher_value_size = 0;
87         var $max_value;
88         var $max_label;
89         var $max_legend_str;
90         var $legend_box_height;
91         var $stream = 'svg'; // stream = 'svg', 'png', 'jpg' or 'gif'
92         var $svg; // The SVG engine class.
93         var $img; // png image
94
95         var $dec1 = 0;
96         var $dec2 = 0;
97         var $fontfile = "";
98         var $encoding;
99         var $out_dir = "";
100         var $dir = "ltr";
101         var $align;
102         var $lang;
103         var $font;
104         var $path;
105         var $bar_width = 0;
106         var $space_between_dots = 0;
107         var $higher_value = 0;
108         var $higher_value_str = null;
109         var $graphic_area_width = 0;
110         var $graphic_area_height = 0;
111         var $graphic_area_x1 = 0;
112         var $graphic_area_x2 = 0;
113         var $graphic_area_y1 = 0;
114         var $graphic_area_y2 = 0;
115         var $legend_exists = false;
116         var $legend_box_width = 0;
117         var $legend_box_x1 = 0;
118         var $legend_box_x2 = 0;
119         var $legend_box_y1 = 0;
120         var $legend_box_y2 = 0;
121
122
123         function __construct($type = 1, $id = 'id', $width = 0, $height = 0)
124         {
125                 global $SysPrefs;
126                 $this->encoding = strtoupper($_SESSION['language']->encoding);
127                 //$this->encoding = "UTF-8"; // Use alternative if used in FA
128                 $this->dir = $_SESSION['language']->dir;
129                 //$this->dir = 'ltr';
130                 $this->lang = $_SESSION['language']->code == 'C' ? 'en' : substr($_SESSION['language']->code, 0, 2);
131                 //$this->lang = 'en';
132                 $this->path = dirname(__FILE__).'/../fonts/';
133                 //$this->path = dirname(__FILE__).'/';
134                 // You can use another UTF-8 font and put it in config.php with the name in $UTF8_fontfile 
135                 if ($this->encoding == 'UTF-8' && !empty($SysPrefs->UTF8_fontfile))
136                         $this->fontfile = $this->path.$SysPrefs->UTF8_fontfile;
137                 elseif ($this->dir == 'rtl')
138                         $this->fontfile = $this->path.'zarnormal.ttf';
139                 else    
140                         $this->fontfile = $this->path.'FreeSans.ttf';
141                 //$this->fontfile = $this->path.'FreeSans.ttf';
142                 //$this->fontfile = $this->path.'zarnormal.ttf';
143                 $this->id = $id;
144                 if ($type == 'bar') $type = 1;
145                 elseif ($type == 'horizontalBar') $type = 2;
146                 elseif ($type == 'dot') $type = 3;
147                 elseif ($type == 'line') $type = 4;
148                 elseif ($type == 'spline') $type = 8;
149                 elseif ($type == 'pie') $type = 5;
150                 elseif ($type == 'donut') $type = 6;
151                 elseif ($type == 'halfDonut') $type = 7;
152                 elseif ($type == 'table') $type = 9;
153                 elseif ($type < 1 || $type > 9) $type = 1;
154                 elseif (!is_numeric($type)) $type = 1;
155                 $this->width = $width;
156                 $this->height = $height;
157                 $this->max_label   = "";
158                 $this->max_value        = NULL;
159                 $this->num_labels = 0;
160                 $this->type = $type;
161                 $this->latin_notation   = false;
162                 $this->align = $this->dir == 'rtl' ? "end" : "start";
163                 $this->font = "Tahoma,Arial,sans-serif";
164                 if ($this->stream != 'svg')
165                 {
166                         $this->tsize = 12;
167                         $this->size = 9;
168                 }
169                 if ($this->dir == 'rtl')
170                 {
171                         $this->size += 1;
172                         $this->tsize += 1;
173                 }
174         }
175
176         function setDirection($dir='ltr')
177         {
178                 $this->dir = $dir;
179         }
180         
181         function setLanguage($lang='en')
182         {
183                 $this->lang = $lang;
184         }
185         
186         function setStream($stream = 'svg')
187         {
188                 $this->stream = $stream;
189                 if ($this->stream == 'png')
190                 {
191                         $this->tsize = 12;
192                         $this->size = 9;
193                 }
194         }
195
196         function setLabels($labels)
197         {
198                 $this->labels = $labels;
199         }
200
201         function addSerie($label, $data, $type = false, $fill = false)
202         {
203                 if ($label == false)
204                         $label = "Serie $this->num_series";
205                 $type = $type == 'line' ? 4 : ($type == 'spline' ? 8 : $type);
206                 $this->series[$this->num_series]['label'] = $label;
207                 $this->series[$this->num_series]['data'] = $data;
208                 $this->series[$this->num_series]['type'] = $type;
209                 $this->series[$this->num_series]['fill'] = $fill;
210                 $this->num_series++;
211         }
212
213         function setXTitle($xtitle)
214         {
215                 $this->axis_x = $xtitle;
216         }
217
218         function setYTitle($ytitle)
219         {
220                 $this->axis_y = $ytitle;
221         }
222
223         function setTitle($title)
224         {
225                 $this->title = $title;
226         }
227
228         function setDTitle($dtitle)
229         {
230                 $this->donut_title = $dtitle; 
231         }
232
233         function setValues($values = false)
234         {
235                 $this->set_values = $values;
236         }
237
238         function setOutDir($dir)
239         {
240                 $this->out_dir = $dir;
241         }
242
243         function isEmpty()
244         {
245                 return (!isset($this->series[0]['data']) || count($this->series[0]['data']) == 0);
246         }
247
248         function display($save="")
249         {
250                 $this->encoding = strtoupper($_SESSION['language']->encoding);
251                 $this->title_height                     = (!empty($this->title)) ? $this->tsize + 15 : 0;
252                 $this->bar_width                        = ($this->type == 1) ? 20 : 15;
253                 $this->space_between_dots       = 40;
254                 $this->higher_value                     = 0;
255                 $this->higher_value_str         = 0;
256                 $this->graphic_area_width       = 0;
257                 $this->graphic_area_height      = 0;
258                 $this->graphic_area_x1          = 30;
259                 $this->graphic_area_y1          = 30 + $this->title_height;
260                 $this->num_labels                       = count($this->labels);
261                 $this->max_legend_str           = "";
262                 $this->legend_box_height  += (14 * $this->num_labels);
263                 for ($i = 0; $i < $this->num_labels; $i++)
264                 {
265                         if (strlen($this->labels[$i]) > MAXLEN)
266                                 $this->labels[$i] = substr($this->labels[$i], 0, MAXLEN)."...";
267                         if (strlen($this->labels[$i]) > strlen($this->max_label))
268                                 $this->max_label = $this->labels[$i];
269                 }
270                 for ($j = 0; $j < $this->num_series; $j++)
271                 {
272                         $this->sum_total[$j] = 0;
273                         if (strlen($this->series[$j]['label']) > strlen($this->max_legend_str))
274                                 $this->max_legend_str = $this->series[$j]['label'];
275                         for ($i = 0; $i < $this->num_labels; $i++)
276                         {
277                                 if ($this->series[$j]['data'][$i] > $this->max_value)
278                                         $this->max_value = $this->series[$j]['data'][$i];
279                                 $this->sum_total[$j] += $this->series[$j]['data'][$i];
280                         }
281                 }
282                 $this->max_value = number_format(round($this->max_value, 1), 1, ".", "");
283                 if ($this->num_series)
284                         $this->legend_exists = true;
285                 if ($this->type == 8 && $this->num_labels < 4)
286                         $this->type = 4;
287                 $this->calculate_higher_value();
288                 $this->calculate_width();
289                 $this->calculate_height();
290                 if ($this->type == 9)
291                 {
292                         $this->draw_table();
293                         return;
294                 }
295                 $this->create_graphic($save);
296         }
297
298         function create_graphic($save="")
299         {
300                 if ($this->stream == 'svg')
301                 {
302                         $this->svg = new SVG($this->width, $this->height, $this->dir, $this->lang);
303                         $this->svg->style("svg{font-family:{$this->font};background-color:inherit}.tips:hover{cursor:pointer;opacity:0.8;}");
304                         $this->load_color_palette($this->type);
305                 }
306                 else
307                 {
308                         $this->img = imagecreatetruecolor($this->width, $this->height);
309                         $this->load_color_palette($this->type);
310                         // Fill background
311                         imagefill($this->img, 0, 0, $this->color['background']);
312                 }
313                 // Draw title
314                 if (!empty($this->title))
315                 {
316                         if ($this->stream == 'svg')
317                         {
318                                 $center = $this->width / 2;
319                                 $this->svg->text($center, 20, $this->title, $this->tsize, $this->color['title'], false, "middle");
320                         }
321                         else
322                         {
323                                 $center = ($this->width / 2) - ($this->string_width($this->title, $this->tsize) / 2);
324                                 $this->_imagestring($this->img, $this->tsize, $center, 10, $this->title, $this->color['title']);
325                         }
326                 }
327                 // Draw axis and background lines for "vertical bars", "dots", "lines" and "splines"
328                 if (preg_match("/^(1|3|4|8)$/", $this->type))
329                 {
330                         if ($this->legend_exists == true)
331                         {
332                                 $this->draw_legend();
333                         }
334                         $higher_value_y    = $this->graphic_area_y1 + (0.1 * $this->graphic_area_height);
335                         $this->higher_value_size = 0.9 * $this->graphic_area_height;
336                         $less  = 7 * strlen($this->higher_value_str);
337                         if ($this->stream == 'svg')
338                         {
339                                 $this->svg->line($this->graphic_area_x1, $higher_value_y, $this->graphic_area_x2, $higher_value_y, $this->color['bg_lines']);
340                                 $this->svg->text($this->graphic_area_x1-$less-7, $higher_value_y, $this->higher_value_str, $this->size, $this->color['axis_values'], false, $this->align);
341                         }
342                         else
343                         {
344                                 imageline($this->img, $this->graphic_area_x1, $higher_value_y, $this->graphic_area_x2, $higher_value_y, $this->color['bg_lines']);
345                                 $this->_imagestring($this->img, $this->size, ($this->graphic_area_x1-$less-7), ($higher_value_y-7), $this->higher_value_str, $this->color['axis_values']);
346                         }
347                         for ($i = 1; $i < 10; $i++)
348                         {
349                                 $dec_y = $i * ($this->higher_value_size / 10);
350                                 $x1 = round($this->graphic_area_x1);
351                                 $y1 = round($this->graphic_area_y2 - $dec_y);
352                                 $x2 = round($this->graphic_area_x2);
353                                 $y2 = $y1;
354                                 if ($this->stream == 'svg')
355                                         $this->svg->line($x1, $y1, $x2, $y2, $this->color['bg_lines']);
356                                 else
357                                         imageline($this->img, $x1, $y1, $x2, $y2, $this->color['bg_lines']);
358                                 if ($i % 2 == 0) 
359                                 {
360                                         $value = $this->number_formated($this->higher_value * $i / 10, $this->dec1);
361                                         $len1 = strlen($this->higher_value_str);
362                                         $len2 = strlen($value);
363                                         if ($len2 < $len1)
364                                                 $len2 += ($len1-$len2-1);
365                                         $less = 7 * $len2;
366                                         if ($this->stream == 'svg')
367                                                 $this->svg->text($x1-$less-7, ($y1), $value, $this->size, $this->color['axis_values'], false, $this->align);
368                                         else
369                                                 $this->_imagestring($this->img, $this->size, ($x1-$less-7), ($y2-7), $value, $this->color['axis_values']);
370                                 }
371                         }
372                         // Axis X
373                         if ($this->stream == 'svg')
374                         {
375                                 $this->svg->text($this->graphic_area_x2+10, $this->graphic_area_y2+10, $this->axis_x, $this->size, $this->color['title'], false, $this->align);
376                                 $this->svg->line($this->graphic_area_x1, $this->graphic_area_y2, $this->graphic_area_x2, $this->graphic_area_y2, $this->color['axis_line']);
377                         }
378                         else
379                         {
380                                 $this->_imagestring($this->img, $this->size, $this->graphic_area_x2+20, $this->graphic_area_y2+3, $this->axis_x, $this->color['title']);
381                                 imageline($this->img, $this->graphic_area_x1, $this->graphic_area_y2, $this->graphic_area_x2, $this->graphic_area_y2, $this->color['axis_line']);
382                         }
383                         // Axis Y
384                         if ($this->stream == 'svg')
385                         {
386                                 $lex = $this->dir == 'rtl' ? 35 : 25; 
387                                 $this->svg->text($lex, $this->graphic_area_y1-10, $this->axis_y, $this->size, $this->color['title'], false, $this->align);
388                                 $this->svg->line($this->graphic_area_x1, $this->graphic_area_y1, $this->graphic_area_x1, $this->graphic_area_y2, $this->color['axis_line']); 
389                         }
390                         else
391                         {
392                                 $this->_imagestring($this->img, $this->size, 10, $this->graphic_area_y1-20, $this->axis_y, $this->color['title']);
393                                 imageline($this->img, $this->graphic_area_x1, $this->graphic_area_y1, $this->graphic_area_x1, $this->graphic_area_y2, $this->color['axis_line']);
394                         }
395                 }
396                 // Draw axis and background lines for "horizontal bars"
397                 else if ($this->type == 2)
398                 {
399                         if ($this->legend_exists == true)
400                         {
401                                 $this->draw_legend();
402                         }
403                         $this->higher_value_size = 0.9 * $this->graphic_area_width;
404                         if ($this->stream == 'svg')
405                         {
406                                 $this->svg->line(($this->graphic_area_x1+$this->higher_value_size), $this->graphic_area_y1, ($this->graphic_area_x1+$this->higher_value_size), $this->graphic_area_y2, $this->color['bg_lines']);
407                                 $this->svg->text((($this->graphic_area_x1+$this->higher_value_size) - ($this->string_width($this->higher_value, $this->size)/2)), ($this->graphic_area_y2 + 20), $this->higher_value_str, $this->size, $this->color['axis_values'], false, $this->align);
408                         }
409                         else
410                         {
411                                 imageline($this->img, ($this->graphic_area_x1+$this->higher_value_size), $this->graphic_area_y1, ($this->graphic_area_x1+$this->higher_value_size), $this->graphic_area_y2, $this->color['bg_lines']);
412                                 $this->_imagestring($this->img, $this->size, (($this->graphic_area_x1+$this->higher_value_size) - ($this->string_width($this->higher_value, $this->size)/2)), ($this->graphic_area_y2+2), $this->higher_value_str, $this->color['axis_values']);
413                         }
414                         for ($i = 1, $alt = 15; $i < 10; $i++)
415                         {
416                                 $dec_x = number_format(round($i * ($this->higher_value_size      / 10), 1), 1, ".", "");
417                                 if ($this->stream == 'svg')
418                                         $this->svg->line(($this->graphic_area_x1+$dec_x), $this->graphic_area_y1, ($this->graphic_area_x1+$dec_x), $this->color['bg_lines']);
419                                 else
420                                         imageline($this->img, ($this->graphic_area_x1+$dec_x), $this->graphic_area_y1, ($this->graphic_area_x1+$dec_x), $this->graphic_area_y2, $this->color['bg_lines']);
421                                 if ($i % 2 == 0) 
422                                 {
423                                         $alt   = (strlen($this->max_value) > 4 && $alt != 15) ? 15 : 2;
424                                         $value = $this->number_formated($this->higher_value * $i / 10, $this->dec1);
425                                         if ($this->stream == 'svg')
426                                                 $this->svg->text((($this->graphic_area_x1+$dec_x) - ($this->string_width($this->higher_value, $this->size)/2)), ($this->graphic_area_y2+$alt+18), $value, $this->size, $this->color['axis_values'], false, $this->align);
427                                         else
428                                                 $this->_imagestring($this->img, $this->size, (($this->graphic_area_x1+$dec_x) - ($this->string_width($this->higher_value, $this->size)/2)), ($this->graphic_area_y2), $value, $this->color['axis_values'], $alt);
429                                 }
430                         }
431                         // Axis X
432                         if ($this->stream == 'svg')
433                         {
434                                 $this->svg->text($this->graphic_area_x2+10, ($this->graphic_area_y2+10), $this->axis_y, $this->size, $this->color['title'], false, $this->align);
435                                 $this->svg->line($this->graphic_area_x1, $this->graphic_area_y2, $this->graphic_area_x2, $this->graphic_area_y2, $this->color['axis_line']);
436                         }
437                         else
438                         {
439                                 $this->_imagestring($this->img, $this->size, ($this->graphic_area_x2+10), ($this->graphic_area_y2+3), $this->axis_y, $this->color['title']);
440                                 imageline($this->img, $this->graphic_area_x1, $this->graphic_area_y2, $this->graphic_area_x2, $this->graphic_area_y2, $this->color['axis_line']);
441                         }
442                         // Axis Y
443                         if ($this->stream == 'svg')
444                         {
445                                 $lex = ($this->string_width($this->max_label, $this->size)) / 2;
446                                 $this->svg->text($lex, ($this->graphic_area_y1-10), $this->axis_x, $this->size, $this->color['title'], false, $this->align);
447                                 $this->svg->line($this->graphic_area_x1, $this->graphic_area_y1, $this->graphic_area_x1, $this->graphic_area_y2, $this->color['axis_line']);
448                         }
449                         else
450                         {
451                                 $this->_imagestring($this->img, $this->size, 20, ($this->graphic_area_y1-20), $this->axis_x, $this->color['title']);
452                                 imageline($this->img, $this->graphic_area_x1, $this->graphic_area_y1, $this->graphic_area_x1, $this->graphic_area_y2, $this->color['axis_line']);
453                         }
454                 }
455                 else if (preg_match("/^(5|6|7)$/", $this->type))
456                 {
457                         // Draw legend box for "pie", "donut" or "half donut"
458                         $this->draw_legend();
459                 }
460                 $average_width = $this->graphic_area_width / $this->num_labels;
461                 $biggest_width = $this->string_width($this->max_label, $this->size) + 10;
462                 if ($biggest_width > $average_width)
463                         $this->angle = -20;
464                 else
465                         $this->angle = 0;
466                 /**
467                 * Draw graphic: VERTICAL BARS
468                 */
469                 if ($this->type == 1)
470                 {
471                         $this->draw_vertical_bars();
472                 }
473                 /**
474                 * Draw graphic: HORIZONTAL BARS
475                 */
476                 else if ($this->type == 2)
477                 {
478                         $this->draw_horizontal_bars();
479                 }
480                 /**
481                 * Draw graphic: DOTS. LINE or SPLINE
482                 */
483                 else if (preg_match("/^(3|4|8)$/", $this->type))
484                 {
485                         $this->draw_lines();
486                         if ($this->stream != 'svg' && $this->type == 8)
487                                 $this->draw_splines();
488                 }
489                 /**
490                 * Draw graphic: PIE, DONUT or HALF DONUT
491                 */
492                 else if (preg_match("/^(5|6|7)$/", $this->type))
493                 {
494                         // Draw PIE, DONUT OR HALF DONUT
495                         $this->draw_pie();
496                 }
497                 if ($this->stream == 'svg')
498                 {
499                         $this->svg->close();
500                         return $this->svg->draw();
501                         //$this->out_dir =      company_path(). '/pdf_files/';
502                         //$filename = $this->out_dir.uniqid().".svg";
503                         //$this->svg->save($filename);
504                         //echo "<img src='$filename' border='0' alt='$this->title'>";
505                 }
506                 else
507                 {
508                         $filename = $this->out_dir.uniqid().".".$this->stream;
509                         if ($this->stream == 'png')
510                                 imagepng($this->img, $save != "" ? $save : $filename);
511                         elseif ($this->stream == 'jpg')
512                                 imagejpeg($this->img, $save != "" ? $save : $filename);
513                         elseif ($this->stream == 'gif') 
514                                 imagegif($this->img, $save != "" ? $save : $filename);
515                         echo "<img src='$filename' border='0' alt='$this->title'>";
516                         /*
517                         header('Content-type: image/png');
518                         imagepng($this->img, NULL, 5);
519                         */
520                         imagedestroy($this->img);
521                 }
522         }
523
524         function draw_vertical_bars()
525         {
526                 $x       = $this->graphic_area_x1 + 10;
527                 $oldx = $oldy = 0;
528                 $p = array();
529                 foreach ($this->labels as $i => $parameter)
530                 {
531                         for ($j = 0; $j < $this->num_series; $j++)
532                         {
533                                 $size = round($this->series[$j]['data'][$i] * $this->higher_value_size / $this->higher_value);
534                                 $x1       = $x;
535                                 $y1       = ($this->graphic_area_y2 - $size) + 1;
536                                 $x2       = $this->bar_width;
537                                 $y2       = $this->graphic_area_y2;
538                                 if ($this->series[$j]['type'] == 8 && $this->num_labels < 4)
539                                         $this->series[$j]['type'] = 4;
540                                 if ($this->stream == 'svg')
541                                 {
542                                         if ($this->series[$j]['type'] == 4 || $this->series[$j]['type'] == 8) // line
543                                         {
544                                                 if (!isset($this->color['line'][0]))
545                                                         $this->load_color_palette($this->series[$j]['type']);
546                                                 if ($i == $this->num_labels - 1)
547                                                 {
548                                                         $dx = ($this->num_series-1) * ($this->bar_width + 10);
549                                                         $xx = $this->graphic_area_x1 + $dx;
550                                                         foreach($this->series[$j]['data'] as $i => $v)
551                                                         {
552                                                                 $size  = round($this->series[$j]['data'][$i] * $this->higher_value_size / $this->higher_value);
553                                                                 $y = $this->graphic_area_y2 - $size;
554                                                                 $p[$i][0] = $xx;
555                                                                 $p[$i][1] = $y;
556                                                                 $xx += $dx;
557                                                         }
558                                                         $path = "M ".$p[0][0].",".$p[0][1];
559                                                         foreach ($p as $ix => $y)
560                                                         {
561                                                                 $this->svg->circle($p[$ix][0], $p[$ix][1], 2.5, $this->color['line'][$j], 2, $this->color['line'][$j], "class=\"tips\"", $this->series[$j]['label'].": ".number_format($this->series[$j]['data'][$ix], 2)); 
562                                                                 if ($ix != 0)
563                                                                 {
564                                                                         if ($this->series[$j]['type'] == 4) // normal line
565                                                                                 $path .= " L".$p[$ix][0].",".$p[$ix][1];
566                                                                         elseif ($this->series[$j]['type'] == 8) // spline
567                                                                                 $path .= $this->_splineCommand($p[$ix], $ix, $p);
568                                                                 }
569                                                         }
570                                                         if ($this->series[$j]['fill'] == true)
571                                                         {
572                                                                 $fpath = $path;
573                                                                 $fpath .= " L ".($p[$ix][0]+1).",".($this->graphic_area_y2-2)." L ".$p[0][0].",".($this->graphic_area_y2-2)." L ".$p[0][0].",".$p[0][1];
574                                                                 $this->svg->path($fpath, $this->color['line_light'][$j], 1, $this->color['line_light'][$j]);
575                                                         }
576                                                         $this->svg->path($path, $this->color['line'][$j], 3, "none");
577                                                 }
578                                         }
579                                         else
580                                         {
581                                                 $this->svg->rect( $x1, $y1, $x2, $y2-$y1, $this->color['bars'][$j], 1, $this->color['bars'][$j], "class=\"tips\"", $this->series[$j]['label'].": ".number_format($this->series[$j]['data'][$i], 2));
582                                                 $x += $this->bar_width + 5;
583                                         }
584                                 }
585                                 else
586                                 {
587                                         if ($this->series[$j]['type'] == 4) // line
588                                         {
589                                                 if (!isset($this->color['line'][0]))
590                                                         $this->load_color_palette($this->series[$j]['type']);
591                                                 if ($i > 0)
592                                                 {
593                                                         $this->_imageline($this->img, $oldx, $oldy, $x1, $y1, $this->color['line'][$j], 3);
594                                                         if ($this->series[$j]['fill'] == true)
595                                                         {
596                                                                 $pt = array($oldx, $oldy+3, $x1, $y1 + 3, $x1, $this->graphic_area_y2-2,        $oldx, $this->graphic_area_y2-2, $oldx, $oldy+4);
597                                                                 imagefilledpolygon($this->img, $pt, 5, $this->color['line_light'][$j]);
598                                                         }
599                                                 }
600                                                 imagefilledrectangle($this->img, $x1-2, $y1-2, $x1+2, $y1+2, $this->color['line'][$j]);
601                                                 $oldx = $x1;
602                                                 $oldy = $y1;
603                                                 $x += 5;
604                                         }
605                                         elseif ($this->series[$j]['type'] == 8) // spline
606                                         {
607                                                 if (!isset($this->color['line'][0]))
608                                                         $this->load_color_palette($this->series[$j]['type']);
609                                                 imagefilledrectangle($this->img, $x1-2, $y1-2, $x1+2, $y1+2, $this->color['line'][$j]);
610                                                 $p[$x1] = $this->graphic_area_y2 - $size;
611                                                 if ($i == $this->num_labels - 1)
612                                                 {
613                                                         $r = $this->imageSpline($p);
614                                                         if ($r == false)
615                                                                 break;
616                                                         while (list ($xx, $yy) = each($r)) 
617                                                         {
618                                                                 imagefilledellipse($this->img, round($xx), round($yy)+1, 3, 3, $this->color['line'][$j]);
619                                                                 if ($this->series[$j]['fill'] == true)
620                                                                 {
621                                                                         $this->_imageline($this->img, round($xx), round($yy+4), round($xx), $this->graphic_area_y2-2, $this->color['line_light'][$j], 3);
622                                                                 }
623                                                         }
624                                                 }
625                                                 $x += 5;
626                                         }
627                                         else
628                                         {
629                                                 $xx2 = $x2 + $x1;
630                                                 imageline($this->img, ($x1+1), ($y1-1), $xx2, ($y1-1), $this->color['bars'][$j]);
631                                                 imageline($this->img, ($xx2+1), ($y1-1), ($xx2+1), $y2, $this->color['bars'][$j]);
632                                                 imageline($this->img, ($xx2+2), ($y1-1), ($xx2+2), $y2, $this->color['bars'][$j]);
633                                                 imagefilledrectangle($this->img, $x1, $y1, $xx2, $y2, $this->color['bars'][$j]);
634                                                 $x += $this->bar_width + 5;
635                                         }
636                                 }
637                                 if ($j == 0)
638                                 {
639                                         if ($this->stream == 'svg')
640                                         {
641                                                 $lex = $this->angle == 0 ? $x1 + 40 : $x1+20;
642                                                 $al = $this->dir == 'rtl' ? "start" : "end";
643                                                 $this->svg->text($lex, ($y2+15), $parameter, $this->size, $this->color['axis_values'], false, $al, $this->angle);
644                                         }
645                                         else
646                                         {
647                                                 $this->_imagestring($this->img, $this->size, $x1, ($y2+2), $parameter, $this->color['axis_values'], 0, $this->angle);
648                                         }
649                                 }
650                         }
651                         $x += 10;
652                 }
653         }
654
655         function draw_horizontal_bars()
656         {
657                 $y = 10;
658                 $label_len = $this->string_width($this->max_label, $this->size);
659                 foreach ($this->labels as $i => $parameter)
660                 {
661                         for ($j = 0; $j < $this->num_series; $j++)
662                         {
663                                 $size = round($this->series[$j]['data'][$i] / $this->higher_value * $this->higher_value_size);
664                                 $x1       = $this->graphic_area_x1 + 1;
665                                 $y1       = $this->graphic_area_y1 + $y;
666                                 $x2       = $x1 + $size;
667                                 $y2       = $y1 + $this->bar_width;
668                                 if ($this->stream == 'svg')
669                                 {
670                                         $this->svg->rect($x1, $y1, $x2-$x1, $y2-$y1, $this->color['bars'][$j], 1, $this->color['bars'][$j], "class=\"tips\"", $this->series[$j]['label'].": ".number_format($this->series[$j]['data'][$i], 2));
671                                         $this->svg->text(($x2+10), ($y1+12), $this->number_formated($this->series[$j]['data'][$i], $this->dec2), $this->size, $this->color['bars'][$j], false, $this->align);
672                                         $y       += $this->bar_width + 1;
673                                 }
674                                 else
675                                 {
676                                         $this->_imageline($this->img, ($x1), ($y2+1), $x2, ($y2+1), $this->color['bars'][$j], 3);
677                                         imagefilledrectangle($this->img, $x1, $y1, $x2, $y2, $this->color['bars'][$j]);
678                                         $this->_imagestring($this->img, $this->size, ($x2+7), ($y1+2), $this->number_formated($this->series[$j]['data'][$i], $this->dec2), $this->color['bars'][$j]);
679                                         $y       += $this->bar_width + 4;
680                                 }
681                         }
682                         if ($this->stream == 'svg')
683                         {
684                                 $lex = $this->dir == 'rtl' ? $label_len : 20;
685                                 $this->svg->text($lex, ($y2-5), $parameter, $this->size, $this->color['axis_values'], false, "start");
686                                 $y += 8;
687                         }
688                         else
689                         {
690                                 $lex = $this->dir == 'rtl' ? $label_len+10 : 20;
691                                 $this->_imagestring($this->img, $this->size, $lex, ($y1-5), $parameter, $this->color['axis_values'], 0, 0, $this->align);
692                                 $y       += 10;
693                         }
694                 }
695         }
696
697         function draw_lines()
698         {
699                 for ($j = 0; $j < $this->num_series; $j++)
700                 {
701                         $x = $this->graphic_area_x1+1;
702                         $oldx = $oldy = 0;
703                         if ($this->type == 8 && $this->num_labels < 4)
704                                 $this->type = 4;
705                         if ($this->stream == 'svg')
706                         {
707                                 $p = array();
708                                 foreach ($this->labels as $i => $parameter)
709                                 {
710                                         $size  = round($this->series[$j]['data'][$i] * $this->higher_value_size / $this->higher_value);
711                                         $y = $this->graphic_area_y2 - $size;
712                                         $p[$i][0] = $x;
713                                         $p[$i][1] = $y;
714                                         if ($j == 0)
715                                         {
716                                                 $lex = $this->angle == 0 ? $x + 30 : $x+10;
717                                                 $al = $this->dir == 'rtl' ? "start" : "end";
718                                                 $this->svg->text($lex, ($this->graphic_area_y2+15), $parameter, $this->size, $this->color['axis_values'], false, $al, $this->angle);
719                                         }
720                                         $x += $this->space_between_dots;
721                                 }
722                                 $path = "M ".$p[0][0].",".$p[0][1];
723                                 foreach ($p as $i => $y)
724                                 {
725                                         $this->svg->circle($p[$i][0], $p[$i][1], 2.5, $this->color['line'][$j], 3, $this->color['line'][$j], "class=\"tips\"", $this->series[$j]['label'].": ".number_format($this->series[$j]['data'][$i], 2)); 
726                                         if ($i != 0)
727                                         {
728                                                 if ($this->type == 4) // normal line
729                                                         $path .= " L".$p[$i][0].",".$p[$i][1];
730                                                 elseif ($this->type == 8) // spline
731                                                         $path .= $this->_splineCommand($p[$i], $i, $p);
732                                         }
733                                 }
734                                 if ($this->series[$j]['fill'] == true)
735                                 {
736                                         $fpath = $path;
737                                         $fpath .= " L ".($p[$i][0]+1).",".($this->graphic_area_y2-1)." L ".$p[0][0].",".($this->graphic_area_y2-1)." L ".$p[0][0].",".$p[0][1];
738                                         $this->svg->path($fpath, $this->color['line_light'][$j], 1, $this->color['line_light'][$j]);
739                                 }
740                                 $this->svg->path($path, $this->color['line'][$j], 3, "none");
741                                 $x += $this->space_between_dots;
742                         }
743                         else
744                         {
745                                 foreach ($this->labels as $i => $parameter)
746                                 {
747                                         $size  = round($this->series[$j]['data'][$i] * $this->higher_value_size / $this->higher_value);
748                                         $y = $this->graphic_area_y2 - $size;
749                                         
750                                         if ($this->type == 4 && $i > 0) // lINES
751                                         {
752                                                 $this->_imageline($this->img, $oldx, $oldy, $x, $y, $this->color['line'][$j], 3);
753                                                 if ($this->series[$j]['fill'] == true)
754                                                 {
755                                                         $pt = array($oldx, $oldy+3, $x, $y + 3, $x, $this->graphic_area_y2-2,   $oldx, $this->graphic_area_y2-2, $oldx, $oldy+4);
756                                                         imagefilledpolygon($this->img, $pt, 5, $this->color['line_light'][$j]);
757                                                 }
758                                         }
759                                         imagefilledrectangle($this->img, $x-2, $y-2, $x+2, $y+2, $this->color['line'][$j]);
760                                         $oldx = $x;
761                                         $oldy = $y;
762                                         if ($j == 0)
763                                                 $this->_imagestring($this->img, $this->size, $oldx, ($this->graphic_area_y2+2), $parameter, $this->color['axis_values'], 0, $this->angle);
764                                         $x += $this->space_between_dots;
765                                 }
766                         }
767                 }
768         }
769
770         function draw_splines()
771         {
772                 for ($j = 0; $j < $this->num_series; $j++)
773                 {
774                         $n = $this->graphic_area_x1+1;
775                         foreach ($this->labels as $i => $parameter)
776                         {
777                                 $size  = round($this->series[$j]['data'][$i] * $this->higher_value_size / $this->higher_value);
778                                 $y = $this->graphic_area_y2 - $size;
779                                 $p[$n] = $y;
780                                 $n += $this->space_between_dots;
781                         }
782                         $r = $this->imageSpline($p);
783                         reset($r);
784                         $oldx = key($r);
785                         $oldy = current($r);
786                         foreach ($r as $x => $y) 
787                         {
788                                 imagefilledellipse($this->img, round($x), round($y)+1, 3, 3, $this->color['line'][$j]);
789
790                                 if ($this->series[$j]['fill'] == true)
791                                 {
792                                         $this->_imageline($this->img, round($x), round($y+4), round($x), $this->graphic_area_y2-2, $this->color['line_light'][$j], 1);
793                                 }
794                         }
795                 }
796         }
797
798         function imageSpline($p, $step = 1, $minx = -1, $maxx = -1)
799         {
800                 $splines = array();
801                 if (count($p) < 4)
802                         return false;
803                 $cx = $cy = $p1 = array();
804                 ksort($p);
805                 foreach ($p as $x => $y)
806                 {
807                         $cx[] = $x;
808                         $cy[] = $y;
809                 }
810                 if ($minx == -1)
811                         $minx = min($cx);
812                 if ($maxx == -1)
813                         $maxx = max($cx);
814                 $n = count($cx);
815                 for ($i = 0; $i < $n; ++$i) 
816                 {
817                         $splines[$i]['x'] = $cx[$i];
818                         $splines[$i]['a'] = $cy[$i];
819                 }
820                 $splines[0]['c'] = $splines[$n - 1]['c'] = 0;
821                 $alpha[0] = $beta[0] = 0;
822                 for ($i = 1; $i < $n - 1; ++$i) 
823                 {
824                         $h_i = $cx[$i] - $cx[$i - 1];
825                         $h_i1 = $cx[$i + 1] - $cx[$i];
826                         $A = $h_i;
827                         $C = 2.0 * ($h_i + $h_i1);
828                         $B = $h_i1;
829                         $F = 6.0 * (($cy[$i + 1] - $cy[$i]) / $h_i1 - ($cy[$i] - $cy[$i - 1]) / $h_i);
830                         $z = ($A * $alpha[$i - 1] + $C);
831                         $alpha[$i] = - $B / $z;
832                         $beta[$i] = ($F - $A * $beta[$i - 1]) / $z;
833                 }
834                 for ($i = $n - 2; $i > 0; --$i) 
835                         $splines[$i]['c'] = $alpha[$i] * $splines[$i + 1]['c'] + $beta[$i];
836                 for ($i = $n - 1; $i > 0; --$i) 
837                 {
838                         $h_i = $cx[$i] - $cx[$i - 1];
839                         $splines[$i]['d'] = ($splines[$i]['c'] - $splines[$i - 1]['c']) / $h_i;
840                         $splines[$i]['b'] = $h_i * (2.0 * $splines[$i]['c'] + $splines[$i - 1]['c']) / 6.0 + ($cy[$i] - $cy[$i - 1]) / $h_i;
841                 }
842                 for ($x = $minx; $x <= $maxx; $x += $step) 
843                 {
844                         $n = count($splines);
845                         if ($x <= $splines[0]['x'])      {
846                                 $s = $splines[1];
847                         } else {
848                                 if ($x >= $splines[$n - 1]['x']) {
849                                         $s = $splines[$n - 1];
850                                 } else {
851                                         $i = 0;
852                                         $j = $n - 1;
853                                         while ($i + 1 < $j) {
854                                                 $k = $i + ($j - $i) / 2;
855                                                 if ($x <= $splines[round($k)]['x']) {
856                                                         $j = $k;
857                                                 } else {
858                                                         $i = $k;
859                                                 }
860                                         }
861                                         $s = $splines[round($j)];
862                                 }
863                         }
864                         $dx = ($x - $s['x']);
865                         $p1[$x] = $s['a'] + ($s['b'] + ($s['c'] / 2.0 + $s['d'] * $dx / 6.0) * $dx) * $dx;
866                 }
867                 return $p1;
868         }
869
870         function draw_pie()
871         {
872                 $n = count(self::$palette);
873                 if ($this->stream == 'svg')
874                 {
875                         $cx = ($this->graphic_area_x1 + $this->graphic_area_x2) / 2;
876                         $cy = ($this->graphic_area_y1 + $this->graphic_area_y2) / 2;
877                         $width    = $this->graphic_area_width;
878                         $height   = $this->graphic_area_height;
879                         if ($this->sum_total[0] == 0)
880                                 $this->sum_total[0] = 1;
881                         $r = $cx * 0.7;
882                         $rc = $this->type == 5 ? 0.01 : $r - 25;
883                         $d = $this->type == 7 ? 180 : 360; // half donut or all
884                         $area = $d / $this->sum_total[0]; 
885                         $start = $this->type == 7 ? 180 : 90;
886                         $rad = pi() / 180.;
887                         $out='';
888                         $gap=0.01;
889                         $ang1=0;          // start angle
890                         foreach ($this->labels as $i => $num)
891                         {
892                                 $n = count(self::$palette);
893                                 $pct[$i] = round($this->series[0]['data'][$i] / $this->sum_total[0] * 100, 1);
894                                 $dang = $this->series[0]['data'][$i] * $area;           // delta angle
895                                 $laf  = $dang > 180 ? 1 : 0; // Large Arc Flag
896                                 $ang2 = $ang1 + $dang;          // second angle
897                                 $a = ($ang1 - $start) * $rad + asin($gap / $rc); 
898                                 $p1 = sprintf('%0.2f,%0.2f', $cx + $rc * cos($a), $cy + $rc * sin($a));
899                                 $a = ($ang1 - $start) * $rad + asin($gap / $r); 
900                                 $p2 = sprintf('%0.2f,%0.2f', $cx + $r * cos($a), $cy + $r * sin($a));
901                                 $a = ($ang2 - $start) * $rad - asin($gap / $r); 
902                                 $p3 = sprintf('%0.2f,%0.2f', $cx + $r * cos($a), $cy + $r * sin($a));
903                                 $a = ($ang2 - $start) * $rad - asin($gap / $rc); 
904                                 $p4 = sprintf('%0.2f,%0.2f', $cx + $rc * cos($a), $cy + $rc * sin($a));
905                                 $a = ($ang1 - $start) * $rad + asin($gap / $rc); 
906                                 $p5 = sprintf('%0.2f,%0.2f', $cx + $rc * cos($a), $cy + $rc * sin($a));
907                                 $this->svg->path("M$p1 L$p2 A$r,$r 0 $laf,1 $p3 L$p4 A$rc,$rc 0 $laf,0 $p5", $this->color[$i % $n], 0, $this->color[$i % $n], "class=\"tips\"", "$num: ".number_format($pct[$i], 1)."&#x25;");
908                                 $ang1=$ang2;
909                         }
910                         if (!empty($this->donut_title) && $this->type != 5)
911                         {
912                                 // Display center text
913                                 if ($this->type == 6)
914                                         $cy += 5;
915                                 $this->svg->text($cx, $cy, $this->donut_title, $this->size, $this->color[0], "style=\"font-size:18px;font-weight:bold;\"", "middle");
916                         }
917                 }
918                 else
919                 {
920                         $start    = 0;
921                         $sizes    = array();
922                         $degrees = $this->type == 7 ? 180 : 360; // half donut or all
923                         foreach ($this->labels as $i => $parameter)
924                         {
925                                 if ($this->sum_total[0] == 0)
926                                         $this->sum_total[0] = 1;
927                                 if ($this->series[0]['data'][$i] < 0)
928                                         $this->series[0]['data'][$i] = 0;
929                                 $size    = $this->series[0]['data'][$i] * $degrees / $this->sum_total[0];
930                                 $size = round($size, 0);
931                                 $sizes[] = $size;
932                                 $start  += $size;
933                         }
934                         $center_x = ($this->graphic_area_x1 + $this->graphic_area_x2) / 2;
935                         $center_y = ($this->graphic_area_y1 + $this->graphic_area_y2) / 2;
936                         $width    = $this->graphic_area_width;
937                         $height   = $this->graphic_area_height;
938                         $start = $this->type == 7 ? 180 : 270;
939                         // Draw pieces
940                         foreach ($sizes as $i => $size)
941                         {
942                                 $j = $i % $n;
943                                 $color = 'arc_' . $j;
944                                 if ($size >= 1)
945                                         imagefilledarc($this->img, $center_x, $center_y, $width, $height, $start, ($start+$size), $this->color[$color], IMG_ARC_PIE);
946                                 $start += $size;
947                         }
948                         if ($this->type != 5) // DONUTS
949                         {
950                                 // 85 pixels width hole
951                                 $start = $this->type == 7 ? 180 : 0; 
952                                 imagefilledarc($this->img, $center_x, $center_y, 85, 85, $start, 360, $this->color['background'], IMG_ARC_PIE);
953                                 imagearc($this->img, $center_x, $center_y, 85, 85, $start, 360, $this->color['bg_legend']);
954                                 imagearc($this->img, $center_x, $center_y, ($width+1), ($height+1), $start, 360, $this->color['bg_legend']);
955                                 if (!empty($this->donut_title))
956                                 {
957                                         $plen = $this->string_width($this->donut_title, $this->size);
958                                         $x1 = $center_x - $plen / 2 - 6;
959                                         $co = $this->type == 7 ? 20 : 10; // HALF or ALL
960                                         $this->_imagestring($this->img, $this->size + 4, $x1, $center_y - $co, $this->donut_title, $this->color['arc_0'], 2, 0);        
961                                         $this->_imagestring($this->img, $this->size + 4, $x1+1, $center_y - $co, $this->donut_title, $this->color['arc_0'], 2, 0);      
962                                 }
963                         }
964                 }
965         }
966
967         function draw_table()
968         {
969                 $cs = $this->num_labels + 1;
970                 $html = "<table cellspacing='0' //style='width:{$this->width}px;font-family:{$this->font};font-size:12px;background:inherit;'>\n";
971                 if (!empty($this->title))
972                         $html .= "<tr><th colspan=$cs style='text-align:center;'><h3>$this->title</th><tr>\n";
973                 $html .= "<tr><th style='border-bottom:1px solid #444'>#</th>"; 
974                 for ($i = 0; $i < $this->num_labels; $i++)
975                         $html .= "<th style='text-align:right;border-bottom:1px solid #444'>{$this->labels[$i]}</th>";
976                 $html .= "</tr>\n";
977                 for ($j = 0; $j < $this->num_series; $j++)
978                 {
979                         $html .= "<tr><td style=''>{$this->series[$j]['label']}</td>";
980                         for ($i = 0; $i < $this->num_labels; $i++)
981                                 $html .= "<td style='text-align:right;'>".number_format($this->series[$j]['data'][$i], 0)."</td>";
982                         $html .= "</tr>\n";
983                 }
984                 $html .= "</table>\n";
985                 echo $html;
986         }
987
988         function calculate_width()
989         {
990                 $this->legend_box_width = ($this->legend_exists == true) ? ($this->string_width($this->max_legend_str, $this->size) + 70) : 0;
991                 switch ($this->type)
992                 {
993                         // Vertical bars or Table
994                         case 1:
995                         case 9:
996                                 $this->graphic_area_x1   += $this->string_width($this->higher_value_str, $this->size);
997                                 if (!empty($this->width)) 
998                                         $this->graphic_area_width = $this->width - $this->graphic_area_x1 - 30 - $this->legend_box_width;
999                                 else
1000                                 {
1001                                         $gap_series = 10;
1002                                         $gap_labels = 5;
1003                                         $serie_width = 0;
1004                                         for ($j = 0; $j < $this->num_series; $j++)
1005                                         {
1006                                                 if ($this->series[$j]['type'] != 4 && $this->series[$j]['type'] != 8)
1007                                                         $serie_width += $this->bar_width;
1008                                                 $serie_width += $gap_labels;
1009                                         }
1010                                         $serie_width += $gap_series;
1011                                         $this->graphic_area_width = $serie_width * $this->num_labels;
1012                                         $this->width += $this->graphic_area_width;
1013                                         $this->width += $this->graphic_area_x1;
1014                                         $this->width += $this->legend_box_width;
1015                                 }
1016                                 break;
1017                         // Horizontal bars
1018                         case 2:
1019                                 $this->graphic_area_x1 += $this->string_width($this->max_label, $this->size);
1020                                 if (!empty($this->width))
1021                                         $this->graphic_area_width = $this->width - $this->graphic_area_x1 - 30 - $this->legend_box_width;
1022                                 else
1023                                 {
1024                                         $this->graphic_area_width = ($this->string_width($this->higher_value_str, $this->size) > 50) ? (5 * ($this->string_width($this->higher_value_str, $this->size)) * 0.85) : 200;
1025                                         $this->width += $this->graphic_area_x1;
1026                                         $this->width += $this->graphic_area_width;
1027                                         $this->width += $this->legend_box_width;
1028                                 }
1029                                 break;
1030                         // Dots
1031                         case 3:
1032                         // Lines
1033                         case 4:
1034                         // Splines
1035                         case 8:
1036                                 $this->graphic_area_x1   += $this->string_width(($this->higher_value_str), $this->size);
1037                                 if (!empty($this->width))
1038                                         $this->graphic_area_width = $this->width - $this->graphic_area_x1 - 30 - $this->legend_box_width;
1039                                 else
1040                                 {
1041                                         $this->graphic_area_width = $this->space_between_dots * ($this->num_labels - 1) + 5;
1042                                         $this->width += $this->graphic_area_width;
1043                                         $this->width += $this->graphic_area_x1 + 30;
1044                                         $this->width += $this->legend_box_width;
1045                                 }
1046                                 break;
1047                         // Pie
1048                         case 5:
1049                         // Donut
1050                         case 6:
1051                         // Half Donut
1052                         case 7:
1053                                 $this->legend_box_width   += 60;
1054                                 if (!empty($this->width))
1055                                         $this->graphic_area_width = $this->width - $this->graphic_area_x1 - 30 - $this->legend_box_width;
1056                                 else
1057                                 {
1058                                         $this->graphic_area_width = 150;
1059                                         $this->width += $this->graphic_area_x1 +20;
1060                                         $this->width += $this->legend_box_width;
1061                                         $this->width += $this->graphic_area_width;
1062                                         $this->width += 90;
1063                                 }
1064                                 break;
1065                 }
1066                 $this->graphic_area_width = max($this->graphic_area_width, $this->string_width($this->title, $this->size));
1067                 $this->graphic_area_x2 = $this->graphic_area_x1 + $this->graphic_area_width;
1068                 $this->legend_box_x1   = $this->graphic_area_x2 + 20;
1069                 $this->legend_box_x2   = $this->legend_box_x1 + $this->legend_box_width;
1070         }
1071
1072         function calculate_height()
1073         {
1074                 switch ($this->type)
1075                 {
1076                         // Vertical bars
1077                         case 1:
1078                                 if (!empty($this->height))
1079                                         $this->graphic_area_height = $this->height - $this->title_height;
1080                                 else
1081                                 {
1082                                         $this->graphic_area_height = 150;
1083                                         $this->height += 65;
1084                                 }
1085                                 break;
1086                    // Horizontal bars
1087                         case 2:
1088                                 if (!empty($this->height))
1089                                         $this->graphic_area_height = $this->height - $this->title_height;
1090                                 else
1091                                 {
1092                                         $gap_labels = 12;
1093                                         if ($this->stream != 'svg')
1094                                                 $gap_labels += 6;
1095                                         $this->graphic_area_height = $this->num_series * $this->bar_width * $this->num_labels + $this->num_labels * $gap_labels;
1096                                         $this->height += 65;
1097                                 }
1098                                 break;
1099                         // Dots
1100                         case 3:
1101                         // Lines
1102                         case 4:
1103                         // Spines
1104                         case 8:
1105                                 if (!empty($this->height))
1106                                         $this->graphic_area_height = $this->height - $this->title_height;
1107                                 else
1108                                 {
1109                                         $this->graphic_area_height = 150;
1110                                         $this->height += 65;
1111                                 }
1112                                 break;
1113                         // Pie
1114                         case 5:
1115                         // Donut
1116                         case 6:
1117                         // Half Donut
1118                         case 7:
1119                                 if (!empty($this->height))
1120                                         $this->graphic_area_height = $this->height - $this->title_height;
1121                                 else
1122                                 {
1123                                         $this->graphic_area_height = 150;
1124                                         $this->height += 65;
1125                                 }
1126                                 break;
1127                 }
1128                 //if ($this->type == 7) // Half Donut;
1129                 $this->graphic_area_y1 -= 10;
1130                 $this->legend_box_y1   = $this->graphic_area_y1;
1131                 $this->height += $this->graphic_area_height + 40;
1132                 $this->graphic_area_y2 = $this->graphic_area_y1 + $this->graphic_area_height;
1133                 $this->legend_box_y2   = $this->legend_box_y1 + $this->legend_box_height;
1134         }
1135
1136         function draw_legend()
1137         {
1138                 $x1 = $this->legend_box_x1;
1139                 $y1 = $this->legend_box_y1;
1140                 $x2 = $this->legend_box_x2;
1141                 $y2 = $this->legend_box_y2;
1142                 if ($this->stream == 'svg')
1143                         $this->svg->rect($x1, $y1, $x2, $y2, $this->color['bg_legend']);
1144                 else
1145                         imagefilledrectangle($this->img, $x1, $y1, $x2, $y2, $this->color['bg_legend']);
1146                 $x = $x1 + 10;
1147                 $y = $y1;
1148                 // Draw legend values for VERTICAL BARS, HORIZONTAL BARS, DOTS, LINES AND SPLINES
1149                 if (preg_match("/^(1|2|3|4|8)$/", $this->type))
1150                 {
1151                         for ($j = 0; $j < $this->num_series; $j++)
1152                         {
1153                                 $color = (preg_match("/^(1|2)$/", $this->type)) ? $this->color['bars'][$j]       : $this->color['line'][$j];
1154                                 $len = $this->string_width($this->max_legend_str, $this->size)-20;
1155                                 if ($this->stream == 'svg')
1156                                 {
1157                                         $lex = $this->dir == 'rtl' ? $x1+$len : $x1-5;
1158                                         $this->svg->circle($lex, $y-5, 6, "#fff", 1, $color);
1159                                         //$this->svg->rect($lex, $y-10, 10, 10, "#fff", 1, $color);
1160                                         $lex = $this->dir == 'rtl' ? $x1+$len-15 : $x-5; 
1161                                         $this->svg->text($lex, ($y-2), $this->series[$j]['label'], $this->size, $this->color['axis_values'], false, "start");
1162                                 }
1163                                 else
1164                                 {
1165                                         $lex = $this->dir == 'rtl' ? $x1+$len+20 : $x-10;
1166                                         imagefilledrectangle($this->img, $lex, $y, ($lex+10), ($y+10), $color);
1167                                         imagerectangle($this->img, $lex, $y, ($lex+10), ($y+10), $this->color['title']);
1168                                         $lex = $this->dir == 'rtl' ? $x1+$len+20 : $x+10;
1169                                         $this->_imagestring($this->img, $this->size, $lex, ($y-2), $this->series[$j]['label'], $this->color['axis_values'], 0, 0, $this->align);
1170                                 }
1171                                 $y += 20;
1172                         }
1173                 }
1174                 // Draw legend values for PIE, DONUT or HALF DONUT
1175                 else if (preg_match("/^(5|6|7)$/", $this->type))
1176                 {
1177                         $n = count(self::$palette);
1178                         if ($this->sum_total[0] == 0)
1179                                 return;
1180                         $label_len = $this->string_width($this->max_label, $this->size);
1181                         //$value_len = $this->string_width($this->max_value, $this->size);
1182                         $value_len = $this->string_width(number_format($this->max_value), $this->size);
1183                         $total_len = $label_len + $value_len;
1184                         $total_len = max($total_len, $x2-$x1);
1185                         if (!empty($this->axis_x)) // Only first serie [0]
1186                         {
1187                                 if ($this->stream == 'svg')
1188                                         $this->svg->text(((($x1+$x1+$total_len)/2) - (strlen($this->axis_x)*7/2)), $y, $this->axis_x." (".$this->series[0]['label'].")", $this->size, $this->color['title'], false, $this->align);
1189                                 else
1190                                         $this->_imagestring($this->img, $this->size, (($x1+$x1+$total_len)/2 - strlen($this->axis_x)*7/2)+30, $y, 
1191                                         $this->axis_x."-".$this->series[0]['label'], $this->color['title']);
1192                                 $y += 25;
1193                         }
1194                         foreach ($this->labels as $i => $parameter)
1195                         {
1196                                 if ($this->set_values)
1197                                         $text = number_format($this->series[0]['data'][$i]);
1198                                 else
1199                                         $text = number_format($this->series[0]['data'][$i] * 100 / $this->sum_total[0], 2) . ' %';
1200                                 if ($this->stream == 'svg')
1201                                 {
1202                                         $j = $i % $n;
1203                                         $lex = $this->dir == 'rtl' ? $x1+$total_len-25 : $x1;
1204                                         $this->svg->circle($lex, $y-5, 6, "#fff", 1, $this->color[$j]);
1205                                         // $this->svg->rect($lex-5, $y-10, 10, 10, "#fff", 1, $this->color[$j]);
1206                                         // Display label text
1207                                         $lex = $this->dir == 'rtl' ? $x1+$total_len-40 : $x1+15;
1208                                         $this->svg->text($lex, ($y-2), $parameter, $this->size, $this->color['axis_values'], false, "start");   
1209                                         $lex = $this->dir == 'rtl' ? $x1+$value_len-25 : $x1+$total_len;
1210                                         $al = $this->dir == 'rtl' ? "start" : "end";
1211                                         $this->svg->text($lex, ($y-2), $text, $this->size, $this->color['axis_values'], false, $al);
1212                                         $lex2 = $this->dir == 'rtl' ? $x1+$total_len-25 : $x1+$total_len;
1213                                         $this->svg->line($x1, $y+1, $lex2, $y+1, $this->color['bg_lines']);
1214                                 }
1215                                 else
1216                                 {
1217                                         $j = $i % $n;
1218                                         $color = 'arc_' . $j;
1219                                         $width = $this->string_width($text, $this->size);
1220                                         imageline($this->img, $x1+15, ($y+11), $x2+20, ($y+11), $this->color['bg_lines']);
1221                                         $lex = $this->dir == 'rtl' ? $x2+20 : $x;
1222                                         imagefilledrectangle($this->img, $lex, $y, ($lex+10), ($y+10), $this->color[$color]);
1223                                         imagerectangle($this->img, $lex, $y, ($lex+10), ($y+10), $this->color['title']);
1224                                         $lex = $this->dir == 'rtl' ? $x2+5 : $x+15;
1225                                         $this->_imagestring($this->img, $this->size, $lex, ($y-2), $parameter, $this->color['axis_values'], 0, 0, $this->align);
1226                                         //$lex = $this->dir == 'rtl' ? $x+10 : $x2-$width;
1227                                         $lex = $this->dir == 'rtl' ? $x1+$value_len : $x2-$width;
1228                                         $al = $this->dir == 'rtl' ? "end" : $this->align;
1229                                         $this->_imagestring($this->img, $this->size, $lex, ($y-2), $text, $this->color['axis_values'], 0, 0, $al);
1230                                 }
1231                                 $y += 18;
1232                         }
1233                 }
1234         }
1235
1236         function string_width($string, $size) 
1237         {
1238                 if ($string == NULL)
1239                         return $size;
1240                 $p = imageftbbox($size, 0, $this->fontfile, $string);
1241                 return $p[4] - $p[0];
1242         }
1243
1244         function calculate_higher_value() 
1245         {
1246                 $digits   = strlen(round($this->max_value));
1247                 $interval = pow(10, ($digits-1));
1248                 $this->higher_value = round(($this->max_value - (round($this->max_value) % $interval) + $interval), 1);
1249                 $this->higher_value_str = $this->number_formated($this->higher_value, $this->dec1);
1250         }
1251
1252         function number_formated($number, $dec_size = 1)
1253         {
1254                 if ($this->latin_notation == true) 
1255                         return number_format(round($number, $dec_size), $dec_size, ",", ".");
1256                 return number_format(round($number, $dec_size), $dec_size, ".", ",");
1257         }
1258
1259         function load_color_palette($type)
1260         {
1261                 // The usual color palette. Change if you like. Must be 6 colors.
1262                 $color = self::$palette;
1263                 if ($this->stream == 'svg')
1264                 {
1265                         $this->color['title']           = sprintf("#%02x%02x%02x",      40,      70, 130);
1266                         $this->color['background']      = sprintf("#%02x%02x%02x", 255, 255, 255);
1267                         $this->color['axis_values'] = sprintf("#%02x%02x%02x",  50,      50,  50);
1268                         $this->color['axis_line']       = sprintf("#%02x%02x%02x", 100, 100, 100);
1269                         $this->color['bg_lines']        = sprintf("#%02x%02x%02x", 220, 220, 220);
1270                         $this->color['bg_legend']       = sprintf("#%02x%02x%02x", 255, 255, 255);
1271                         $this->color['shadow']          = sprintf("#%02x%02x%02x", 255, 255, 255);
1272
1273                         if (preg_match("/^(1|2)$/", $type)) // Vertical Bar or Horizontal bar
1274                         {
1275                                 for ($j = 0; $j < $this->num_series; $j++)
1276                                 {
1277                                         $this->color['bars'][$j] = $color[$j];
1278                                 }
1279                         }
1280                         elseif (preg_match("/^(3|4|8)$/", $type)) // Dots, Lines or Splines
1281                         {
1282                                 for ($j = 0; $j < $this->num_series; $j++)
1283                                 {
1284                                         $this->color['line'][$j] = $color[$j];
1285                                         $this->color['line_light'][$j] = $this->colorLuminate($color[$j], 0.8);
1286                                 }
1287                         }
1288                         elseif (preg_match("/^(5|6|7)$/", $type)) // Pie, Donut or Half Donut
1289                         {
1290                                 $n = count($color);
1291                                 for ($j = 0; $j < $n; $j++) 
1292                                         $this->color[$j] = $color[$j];
1293                         }
1294                 }
1295                 else
1296                 {
1297                         $this->color['title']           = imagecolorallocate($this->img,  40,  70,      130);
1298                         $this->color['background']      = imagecolorallocate($this->img, 255, 255, 255);
1299                         $this->color['axis_values'] = imagecolorallocate($this->img,  50,  50,  50);
1300                         $this->color['axis_line']       = imagecolorallocate($this->img, 100, 100, 100);
1301                         $this->color['bg_lines']        = imagecolorallocate($this->img, 220, 220, 220);
1302                         $this->color['bg_legend']       = imagecolorallocate($this->img, 255, 255, 255);
1303                         $this->color['shadow']          = imagecolorallocate($this->img, 255, 255, 255);
1304
1305                         if (preg_match("/^(1|2)$/", $type)) // Vertical Bar or Horizontal bar
1306                         {
1307                                 for ($j = 0; $j < $this->num_series; $j++)
1308                                 {
1309                                         $c = $this->hex2rgb($color[$j]);
1310                                         $this->color['bars'][$j] = imagecolorallocate($this->img, $c[0], $c[1], $c[2]);
1311                                 }
1312                         }
1313                         else if (preg_match("/^(3|4|8)$/", $type)) // Dots, Lines or Splines
1314                         {
1315                                 for ($j = 0; $j < $this->num_series; $j++)
1316                                 {
1317                                         $c = $this->hex2rgb($color[$j]);
1318                                         $this->color['line'][$j] = imagecolorallocate($this->img, $c[0], $c[1], $c[2]);
1319                                         $c = $this->hex2rgb($this->colorLuminate($color[$j], 0.8)); // lighten for Areas
1320                                         $this->color['line_light'][$j] = imagecolorallocate($this->img, $c[0], $c[1], $c[2]);
1321                                 }
1322                         }
1323                         else if (preg_match("/^(5|6|7)$/", $type)) // Pie, Donut or Half Donut
1324                         {
1325                                 $n = count($color); 
1326                                 for ($j = 0; $j < $n; $j++) 
1327                                 {
1328                                         $c = $this->hex2rgb($color[$j]);
1329                                         $this->color["arc_{$j}"] = imagecolorallocate($this->img, $c[0], $c[1], $c[2]);
1330                                 }
1331                         }
1332                 }
1333         }
1334
1335         function colorLuminate($hex, $percent) // Color lightner, minus is darker
1336         {
1337                 $hex = ltrim($hex, '#');
1338                 if (strlen($hex) == 3) {
1339                         $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
1340                 }
1341                 $hex = array_map('hexdec', str_split($hex, 2));
1342                 foreach ($hex as &$color) {
1343                         $limit = $percent < 0 ? $color : 255 - $color;
1344                         $value = ceil($limit * $percent);
1345                         $color = str_pad(dechex($color + $value), 2, '0', STR_PAD_LEFT);
1346                 }
1347                 return '#' . implode($hex);
1348         }
1349         
1350         function _imageline($image, $x1, $y1, $x2, $y2, $color, $thick = 1)
1351         {
1352                 if ($thick == 1) 
1353                         return imageline($image, $x1, $y1, $x2, $y2, $color);
1354                 $t = $thick / 2 - 0.5;
1355                 if ($x1 == $x2 || $y1 == $y2)
1356                         return imagefilledrectangle($image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color);
1357                 $k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q
1358                 $a = $t / sqrt(1 + pow($k, 2));
1359                 $points = array(
1360                         round($x1 - (1+$k)*$a), round($y1 + (1-$k)*$a),
1361                         round($x1 - (1-$k)*$a), round($y1 - (1+$k)*$a),
1362                         round($x2 + (1+$k)*$a), round($y2 - (1-$k)*$a),
1363                         round($x2 + (1-$k)*$a), round($y2 + (1+$k)*$a),
1364                 );
1365                 if (PHP_MAJOR_VERSION >= 8) { // num_points is deprecated
1366                         imagefilledpolygon($image, $points, $color);
1367                         return imagepolygon($image, $points, $color);
1368                 }
1369                 else {
1370                         imagefilledpolygon($image, $points, 4, $color);
1371                         return imagepolygon($image, $points, 4, $color);
1372                 }
1373         }
1374
1375         function _imagestring($img, $size, $x, $y, $string, $col, $alt=0, $angle=0, $align=false)
1376         {
1377                 if ($align == 'end')
1378                 {
1379                         $p = imagettfbbox($size, $angle, $this->fontfile, $string); 
1380                         $x -= $p[2];
1381                 }
1382                         
1383                 if ($this->encoding != 'UTF-8') 
1384                 {
1385                         if (function_exists('iconv'))
1386                                 $string = iconv($this->encoding, 'UTF-8', $string);
1387                         else
1388                                 $string = mb_convert_encoding($string, 'UTF-8', $this->encoding);
1389                 }
1390                 
1391                 // Handling ev. RTL languages
1392                 if ($alt)
1393                 {
1394                         if ($this->encoding == 'UTF-8' && $this->dir == 'rtl')
1395                                 $alt_len = 18;
1396                         else
1397                                 $alt_len = 12;
1398                         if (strlen($string) > $alt_len)
1399                                 $string = substr($string, 0, $alt_len);
1400                 }               
1401                 if ($this->encoding == 'UTF-8')
1402                 {
1403                         if (is_arabic($string))
1404                                 $string = arabic($string);
1405                         elseif (is_hebrew($string))
1406                                 $string = hebrew($string);
1407                 }
1408                 $y += $size + 3;        
1409                 if ($this->encoding == 'UTF-8' && is_arabic($string))
1410                 {
1411                         $size += 2;
1412                         $string = str_replace(" ", "  ", $string);
1413                 }       
1414                 imagettftext($img, $size, $angle, round($x), $y  + $alt, $col, $this->fontfile, $string);
1415         }
1416
1417         function hex2rgb($hex)
1418         {
1419                 list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
1420                 return array($r, $g, $b);
1421         }
1422
1423         function _controlPoint($curr, $prev, $next, $reverse = false)
1424         {
1425                 $p = $prev ? $prev : $curr;
1426                 $n = $next ? $next : $curr;
1427                 $smooth = 0.2;
1428                 $lenx = $n[0] - $p[0];
1429                 $leny = $n[1] - $p[1];
1430                 $len = sqrt(pow($lenx, 2) + pow($leny, 2));
1431                 $angle = atan2($leny, $lenx);
1432                 $len *= $smooth;
1433                 $angle += ($reverse ? pi() : 0);
1434                 $x = $curr[0] + cos($angle) * $len;
1435                 $y = $curr[1] + sin($angle) * $len;
1436                 return array($x, $y);
1437         }
1438
1439         function _splineCommand($p, $i, $a)
1440         {
1441                 $c = isset($a[$i - 1]) ? $a[$i - 1] : false;
1442                 $d = isset($a[$i - 2]) ? $a[$i - 2] : false;
1443                 $e = isset($a[$i + 1]) ? $a[$i + 1] : false;
1444                 $cps = $this->_controlPoint($c, $d, $p);
1445                 $cpe = $this->_controlPoint($p, $c, $e, true);
1446                 return " C".round($cps[0],1).",".round($cps[1],1)." ".round($cpe[0],1).",".round($cpe[1], 1)." $p[0],$p[1]";
1447         }
1448
1449         function clean_out_dir()
1450         {
1451                 $dir = $this->out_dir;
1452                 if ($d = @opendir($dir)) {
1453                         while (($file = readdir($d)) !== false) {
1454                                 if (!is_file($dir.'/'.$file) || $file == 'index.php') continue;
1455                                 // then check to see if this one is too old
1456                                 $ftime = filemtime($dir.'/'.$file);
1457                                 // seems 3 min is enough for any report download, isn't it?
1458                                 if (time()-$ftime > 180){
1459                                         unlink($dir.'/'.$file);
1460                                 }
1461                         }
1462                         closedir($d);
1463                 }
1464         }
1465 }
1466
1467 class SVG
1468 {
1469         var $svg;
1470
1471         function __construct($width=0, $height=0, $dir='ltr', $lang='en')
1472         {
1473                 $this->svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" direction=\"$dir\" xml:lang=\"$lang\" width=\"$width\" height=\"$height\" viewPort=\"0 0 $width $height\"";
1474                 $this->svg .= ">\n";
1475         }
1476         function circle($x, $y, $r, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1477         {
1478                 $this->svg .= "<circle cx=\"$x\" cy=\"$y\" r=\"$r\"";
1479                 if (!empty($line_width))
1480                         $this->svg .= " stroke-width=\"$line_width\"";
1481                 if (!empty($bcolor))
1482                         $this->svg .= " stroke=\"$bcolor\"";
1483                 if (empty($color))
1484                         $color = "none";
1485                 $this->svg .= " fill=\"$color\"";
1486                 if (!empty($style))
1487                         $this->svg .= " $style";
1488                 if (!empty($tooltip))
1489                         $this->svg .= "><title>$tooltip</title></circle>\n";
1490                 else
1491                         $this->svg .= " />\n";
1492         }
1493         function rect($x1, $y1, $x2, $y2, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1494         {
1495                 $this->svg .= "<rect x=\"$x1\" y=\"$y1\" width=\"$x2\" height=\"$y2\"";
1496                 if (!empty($line_width))
1497                         $this->svg .= " stroke-width=\"$line_width\"";
1498                 if (!empty($bcolor))
1499                         $this->svg .= " stroke=\"$bcolor\"";
1500                 if (empty($color))
1501                         $color = "none";
1502                 $this->svg .= " fill=\"$color\"";
1503                 if (!empty($style))
1504                         $this->svg .= " $style";
1505                 if (!empty($tooltip))
1506                         $this->svg .= "><title>$tooltip</title></rect>\n";
1507                 else
1508                         $this->svg .= " />\n";
1509         }
1510         function line($x1, $y1, $x2, $y2, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1511         {
1512                 $this->svg .= "<line x1=\"$x1\" y1=\"$y1\" x2=\"$x2\" y2=\"$y2\"";
1513                 if (!empty($line_width))
1514                         $this->svg .= " stroke-width=\"$line_width\"";
1515                 if (!empty($bcolor))
1516                         $this->svg .= " stroke=\"$bcolor\"";
1517                 if (empty($color))
1518                         $color = "none";
1519                 $this->svg .= " fill=\"$color\"";
1520                 if (!empty($style))
1521                         $this->svg .= " $style";
1522                 if (!empty($tooltip))
1523                         $this->svg .= "><title>$tooltip</title></line>\n";
1524                 else
1525                         $this->svg .= " />\n";
1526         }
1527         function polygon($p, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1528         {
1529                 $this->svg .= "<polygon points=\"$p\"";
1530                 if (!empty($line_width))
1531                         $this->svg .= " stroke-width=\"$line_width\"";
1532                 if (!empty($bcolor))
1533                         $this->svg .= " stroke=\"$bcolor\"";
1534                 if (empty($color))
1535                         $color = "none";
1536                 $this->svg .= " fill=\"$color\"";
1537                 if (!empty($style))
1538                         $this->svg .= " $style";
1539                 if (!empty($tooltip))
1540                         $this->svg .= "><title>$tooltip</title></polygon>\n";
1541                 else
1542                         $this->svg .= " />\n";
1543         }
1544         function polyline($p, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1545         {
1546                 $this->svg .= "<polyline points=\"$p\"";
1547                 if (!empty($line_width))
1548                         $this->svg .= " stroke-width=\"$line_width\"";
1549                 if (!empty($bcolor))
1550                         $this->svg .= " stroke=\"$bcolor\"";
1551                 if (empty($color))
1552                         $color = "none";
1553                 $this->svg .= " fill=\"$color\"";
1554                 if (!empty($style))
1555                         $this->svg .= " $style";
1556                 if (!empty($tooltip))
1557                         $this->svg .= "><title>$tooltip</title></polyline>\n";
1558                 else
1559                         $this->svg .= " />\n";
1560         }
1561         function path($d, $bcolor=false, $line_width=1, $color=false, $style=false, $tooltip=false)
1562         {
1563                 $this->svg .= "<path d=\"$d\"";
1564                 if (!empty($line_width))
1565                         $this->svg .= " stroke-width=\"$line_width\"";
1566                 if (!empty($bcolor))
1567                         $this->svg .= " stroke=\"$bcolor\"";
1568                 if (empty($color))
1569                         $color = "none";
1570                 $this->svg .= " fill=\"$color\"";
1571                 if (!empty($style))
1572                         $this->svg .= " $style";
1573                 if (!empty($tooltip))
1574                         $this->svg .= "><title>$tooltip</title></path>\n";
1575                 else
1576                         $this->svg .= " />\n";
1577         }
1578         function text($x, $y, $text, $size=false, $color=false, $style=false, $align=false, $angle=false) // align start, middle, end. 
1579         {
1580                 $this->svg .= "<text x=\"$x\" y=\"$y\"";
1581                 if (!empty($color))
1582                         $this->svg .= " fill=\"$color\"";
1583                 if (!empty($size))
1584                         $this->svg .= " font-size=\"$size\"";
1585                 if (!empty($style))
1586                         $this->svg .= " $style";
1587                 if (!empty($align))
1588                         $this->svg .= " text-anchor=\"$align\"";
1589                 if (!empty($angle))
1590                         $this->svg .= " transform=\"rotate($angle $x $y)\"";
1591                 $this->svg .= ">$text</text>\n";
1592         }
1593         function other($s)
1594         {
1595                 $this->svg .= $s;
1596         }
1597         function style($s='')
1598         {
1599                 $this->svg .= "<defs><style type=\"text/css\"><![CDATA[ $s ]]></style></defs>\n";
1600         }
1601         function open_group($p = '')
1602         {
1603                 $this->svg .= "<g $p>\n";
1604         }
1605         function close_group()
1606         {
1607                 $this->svg .= "</g>\n";
1608         }
1609         function close()
1610         {
1611                 $this->svg .= "</svg>\n";
1612         }
1613         function draw()
1614         {
1615                 //header('Content-Type: image/svg+xml');
1616                 echo $this->svg;
1617                 return true;
1618         }
1619         function save($fileName)
1620         {
1621                 file_put_contents($fileName, $this->svg);
1622         }
1623 }
1624
1625 // The following is for handling RTL texts. GD does not handle RTL at all.
1626 // The function, arabic, has been taken from Milad Rastian and
1627 // modified by Bagram Siadat.
1628 // The function has been further modified and several bugs are fixed.
1629
1630 function is_arabic($text)
1631 {
1632         return preg_match('/\p{Arabic}/u', $text);
1633
1634
1635 function is_hebrew($text)
1636 {
1637         return preg_match('/\p{Hebrew}/u', $text);
1638
1639
1640 function utf8_strlen($str) 
1641 {
1642         return preg_match_all('/[\x00-\x7F\xC0-\xFD]/', $str, $dummy);
1643 }
1644
1645 function utf8_chr($uni) 
1646 {
1647         $r = "";
1648         # ASCII range (including control chars)
1649         if ( ($uni >= 0) && ($uni <= 0x007f) ) 
1650                         $r .= chr($uni);
1651         elseif ($uni <= 0x07ff) // 2 byte sequence
1652         {
1653                 $r .= chr(0xc0 | ($uni >> 6));
1654                 $r .= chr(0x80 | ($uni & 0x003f));
1655         } 
1656         elseif($uni == 0xFEFF) // Byte order mark (skip)
1657                 return chr(0); // nop -- zap the BOM
1658         elseif ($uni >= 0xD800 && $uni <= 0xDFFF) // Test for illegal surrogates 
1659                 return chr(0); // found a surrogate
1660         elseif ($uni <= 0xffff) // 3 byte sequence 
1661         {
1662                 $r .= chr(0xe0 | ($uni >> 12));
1663                 $r .= chr(0x80 | (($uni >> 6) & 0x003f));
1664                 $r .= chr(0x80 | ($uni & 0x003f));
1665         } 
1666         elseif ($uni <= 0x10ffff) // 4 byte sequence 
1667         {
1668                 $r .= chr(0xf0 | ($uni >> 18));
1669                 $r .= chr(0x80 | (($uni >> 12) & 0x3f));
1670                 $r .= chr(0x80 | (($uni >> 6) & 0x3f));
1671                 $r .= chr(0x80 | ($uni & 0x3f));
1672         } 
1673         else 
1674                 return chr(0);
1675         return $r;
1676 }
1677
1678 ///
1679 function arabic($str,$z="",$method='normal')
1680 {
1681 $p_chars = array (
1682         utf8_chr(0x0622) => array (utf8_chr(0xfe82), utf8_chr(0xfe82), utf8_chr(0x0622)),
1683         utf8_chr(0x0627) => array (utf8_chr(0xfe8e), utf8_chr(0xfe8e), utf8_chr(0x0627)),
1684         utf8_chr(0x0628) => array (utf8_chr(0xfe90), utf8_chr(0xfe92), utf8_chr(0xfe91)),
1685         utf8_chr(0x067e) => array (utf8_chr(0xfb57), utf8_chr(0xfb59), utf8_chr(0xfb58)),
1686         utf8_chr(0x062a) => array (utf8_chr(0xfe96), utf8_chr(0xfe98), utf8_chr(0xfe97)),
1687         utf8_chr(0x062b) => array (utf8_chr(0xfe9a), utf8_chr(0xfe9c), utf8_chr(0xfe9b)),
1688         utf8_chr(0x062c) => array (utf8_chr(0xfe9e), utf8_chr(0xfea0), utf8_chr(0xfe9f)),
1689         utf8_chr(0x0686) => array (utf8_chr(0xfb7b), utf8_chr(0xfb7d), utf8_chr(0xfb7c)),
1690         utf8_chr(0x062d) => array (utf8_chr(0xfea2), utf8_chr(0xfea4), utf8_chr(0xfea3)),
1691         utf8_chr(0x062e) => array (utf8_chr(0xfea6), utf8_chr(0xfea8), utf8_chr(0xfea7)),
1692         utf8_chr(0x062f) => array (utf8_chr(0xfeaa), utf8_chr(0xfeaa), utf8_chr(0xfea9)),
1693         utf8_chr(0x0630) => array (utf8_chr(0xfeac), utf8_chr(0xfeac), utf8_chr(0xfeab)),
1694         utf8_chr(0x0631) => array (utf8_chr(0xfeae), utf8_chr(0xfeae), utf8_chr(0xfead)),
1695         utf8_chr(0x0632) => array (utf8_chr(0xfeb0), utf8_chr(0xfeb0), utf8_chr(0xfeaf)),
1696         utf8_chr(0x0698) => array (utf8_chr(0xfb8b), utf8_chr(0xfb8b), utf8_chr(0xfb8a)),
1697         utf8_chr(0x0633) => array (utf8_chr(0xfeb2), utf8_chr(0xfeb4), utf8_chr(0xfeb3)),
1698         utf8_chr(0x0634) => array (utf8_chr(0xfeb6), utf8_chr(0xfeb8), utf8_chr(0xfeb7)),
1699         utf8_chr(0x0635) => array (utf8_chr(0xfeba), utf8_chr(0xfebc), utf8_chr(0xfebb)),
1700         utf8_chr(0x0636) => array (utf8_chr(0xfebe), utf8_chr(0xfec0), utf8_chr(0xfebf)),
1701         utf8_chr(0x0637) => array (utf8_chr(0xfec2), utf8_chr(0xfec4), utf8_chr(0xfec3)),
1702         utf8_chr(0x0638) => array (utf8_chr(0xfec6), utf8_chr(0xfec8), utf8_chr(0xfec7)),
1703         utf8_chr(0x0639) => array (utf8_chr(0xfeca), utf8_chr(0xfecc), utf8_chr(0xfecb)),
1704         utf8_chr(0x063a) => array (utf8_chr(0xfece), utf8_chr(0xfed0), utf8_chr(0xfecf)),
1705         utf8_chr(0x0641) => array (utf8_chr(0xfed2), utf8_chr(0xfed4), utf8_chr(0xfed3)),
1706         utf8_chr(0x0642) => array (utf8_chr(0xfed6), utf8_chr(0xfed8), utf8_chr(0xfed7)),
1707         utf8_chr(0x06a9) => array (utf8_chr(0xfeda), utf8_chr(0xfedc), utf8_chr(0xfedb)),
1708         utf8_chr(0x06af) => array (utf8_chr(0xfb93), utf8_chr(0xfb95), utf8_chr(0xfb94)),
1709         utf8_chr(0x0644) => array (utf8_chr(0xfede), utf8_chr(0xfee0), utf8_chr(0xfedf)),
1710         utf8_chr(0x0645) => array (utf8_chr(0xfee2), utf8_chr(0xfee4), utf8_chr(0xfee3)),
1711         utf8_chr(0x0646) => array (utf8_chr(0xfee6), utf8_chr(0xfee8), utf8_chr(0xfee7)),
1712         utf8_chr(0x0648) => array (utf8_chr(0xfeee), utf8_chr(0xfeee), utf8_chr(0xfeed)),
1713         utf8_chr(0x06cc) => array (utf8_chr(0xfbfd), utf8_chr(0xfbff), utf8_chr(0xfbfe)),
1714         utf8_chr(0x0643) => array (utf8_chr(0xfeda), utf8_chr(0xfedc), utf8_chr(0xfedb)),
1715         utf8_chr(0x064a) => array (utf8_chr(0xfef2), utf8_chr(0xfef4), utf8_chr(0xfef3)),
1716         utf8_chr(0x0623) => array (utf8_chr(0xfe84), utf8_chr(0xfe84), utf8_chr(0xfe83)),
1717         utf8_chr(0x0624) => array (utf8_chr(0xfe86), utf8_chr(0xfe86), utf8_chr(0xfe85)),
1718         utf8_chr(0x0625) => array (utf8_chr(0xfe88), utf8_chr(0xfe88), utf8_chr(0xfe87)),
1719         utf8_chr(0x0626) => array (utf8_chr(0xfe8a), utf8_chr(0xfe8c), utf8_chr(0xfe8b)),
1720         utf8_chr(0x0629) => array (utf8_chr(0xfe94), utf8_chr(0xfe98), utf8_chr(0xfe97))
1721 );
1722
1723 $nastaligh = array (
1724         utf8_chr(0x0647) => array (utf8_chr(0xfbab), utf8_chr(0xfbad), utf8_chr(0xfbac))
1725 );
1726
1727 $normal = array (
1728         utf8_chr(0x0647) => array (utf8_chr(0xfeea), utf8_chr(0xfeec), utf8_chr(0xfeeb))
1729 );
1730
1731 $mp_chars = array (utf8_chr(0x0622), utf8_chr(0x0627), utf8_chr(0x062f), utf8_chr(0x0630), utf8_chr(0x0631), utf8_chr(0x0632), 
1732         utf8_chr(0x0698), utf8_chr(0x0648), utf8_chr(0x0623), utf8_chr(0x0625), utf8_chr(0x0624));
1733
1734 $ignorelist = array (utf8_chr(0x0000), utf8_chr(0x064c), utf8_chr(0x064d), utf8_chr(0x064b), utf8_chr(0x064f), utf8_chr(0x0650), 
1735         utf8_chr(0x064e), utf8_chr(0x0651), utf8_chr(0x0653), utf8_chr(0x0670), utf8_chr(0x0654), utf8_chr(0xfe76), utf8_chr(0xfe7a), 
1736         utf8_chr(0xfe78), utf8_chr(0xfe7c), utf8_chr(0xfe7e), utf8_chr(0xfe74), utf8_chr(0xfe70), utf8_chr(0xfc5e), utf8_chr(0xfc5f), 
1737         utf8_chr(0xfc60), utf8_chr(0xfc61), utf8_chr(0xfc62), utf8_chr(0xfc63));
1738
1739         $str_back = $output = $e_output = $str_next = $str1 = $num = "";
1740         if ($method == 'nastaligh')
1741                 $p_chars = array_merge($p_chars, $nastaligh);
1742         else
1743                 $p_chars = array_merge($p_chars, $normal);
1744         $str_len = utf8_strlen($str);
1745         preg_match_all("/./u", $str, $ar);
1746         for ($i = 0; $i < $str_len; $i++)
1747         {
1748                 if (isset($ar[0][$i]))
1749                         $str1 = $ar[0][$i];
1750                 if(isset($ar[0][$i+1]) && in_array($ar[0][$i+1], $ignorelist))
1751                 {
1752                         if (isset($ar[0][$i+2]))
1753                                 $str_next = $ar[0][$i+2];
1754                         if ($i == 2) 
1755                                 $str_back = $ar[0][$i-2];
1756                         if ($i > 1 && $i != 2) 
1757                                 $str_back = $ar[0][$i-1];
1758                 }
1759                 elseif ($i > 0 && isset($ar[0][$i-1]) && !in_array($ar[0][$i-1], $ignorelist))
1760                 {
1761                         if (isset($ar[0][$i+1]))
1762                                 $str_next = $ar[0][$i+1];
1763                         if ($i != 0) 
1764                                 $str_back = $ar[0][$i-1];
1765                 }
1766                 else
1767                 {
1768                         if (isset($ar[0][$i+1]) && !empty($ar[0][$i+1]))
1769                                 $str_next = $ar[0][$i+1];
1770                         elseif ($i > 0 && isset($ar[0][$i-1])) 
1771                                 $str_next = $ar[0][$i-1];
1772                         if ($i > 1 && isset($ar[0][$i-2])) 
1773                                 $str_back = $ar[0][$i-2];
1774                 }
1775                 if (!in_array($str1,$ignorelist))
1776                 {
1777                         if (array_key_exists($str1,$p_chars))
1778                         {
1779                                 if (!$str_back || $str_back==" " || !array_key_exists($str_back,$p_chars))
1780                                 {
1781                                         if (!array_key_exists($str_back, $p_chars) && !array_key_exists($str_next, $p_chars)) 
1782                                                 $output = $str1.$output;
1783                                         else 
1784                                                 $output = $p_chars[$str1][2].$output;
1785                                         continue;
1786                                 }
1787                                 elseif (array_key_exists($str_next, $p_chars) && array_key_exists($str_back, $p_chars))
1788                                 {
1789                                         if (in_array($str_back, $mp_chars) && array_key_exists($str_next, $p_chars))
1790                                                 $output = $p_chars[$str1][2].$output;
1791                                         else
1792                                                 $output = $p_chars[$str1][1].$output;
1793                                         continue;
1794                                 }
1795                                 elseif (array_key_exists($str_back, $p_chars) && !array_key_exists($str_next, $p_chars))
1796                                 {
1797                                         if (in_array($str_back, $mp_chars))
1798                                                 $output = $str1.$output;
1799                                         else
1800                                                 $output = $p_chars[$str1][0].$output;
1801                                         continue;
1802                                 }
1803                         }
1804                         elseif ($z == "we")
1805                         {
1806                                 $number = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1807                                         "\xD9\xA0", "\xD9\xA1", "\xD9\xA2", "\xD9\xA3", "\xD9\xA4", "\xD9\xA5", "\xD9\xA6", 
1808                                         "\xD9\xA7", "\xD9\xA8", "\xD9\xA9",     "\xDB\xB0", "\xDB\xB1", "\xDB\xB2", "\xDB\xB3", 
1809                                         "\xDB\xB4", "\xDB\xB5", "\xDB\xB6", "\xDB\xB7", "\xDB\xB8", "\xDB\xB9");
1810                                 if (in_array($str1, $number))
1811                                 {
1812                                         $num .= $str1;
1813                                         $str1 = "";
1814                                 }
1815                                 if (!in_array($str_next, $number))
1816                                 {
1817                                         $str1 .= $num;
1818                                         $num = "";
1819                                 }
1820                                 //$output = $str1.$output;
1821                                 $output = $output.$str1;
1822                         }
1823                         elseif ($z == "fa")
1824                         {
1825                                 $number = array (utf8_chr(0x0660), utf8_chr(0x0661), utf8_chr(0x0662), 
1826                                         utf8_chr(0x0663), utf8_chr(0x0664), utf8_chr(0x0665), utf8_chr(0x0666), 
1827                                         utf8_chr(0x0667), utf8_chr(0x0668), utf8_chr(0x0669), utf8_chr(0x06f4), 
1828                                         utf8_chr(0x06f5), utf8_chr(0x06f6), utf8_chr(0x0030), utf8_chr(0x0031), 
1829                                         utf8_chr(0x0032), utf8_chr(0x0033), utf8_chr(0x0034), utf8_chr(0x0035), 
1830                                         utf8_chr(0x0036), utf8_chr(0x0037), utf8_chr(0x0038), utf8_chr(0x0039));
1831                                 switch ($str1)
1832                                 {
1833                                         case ")" : 
1834                                                 $str1 = "("; 
1835                                                 break;
1836                                         case "(" : 
1837                                                 $str1 = ")"; 
1838                                                 break;
1839                                         case "}" : 
1840                                                 $str1 = "{"; 
1841                                                 break;
1842                                         case "{" : 
1843                                                 $str1 = "}"; 
1844                                                 break;
1845                                         case "]" : 
1846                                                 $str1 = "["; 
1847                                                 break;
1848                                         case "[" : 
1849                                                 $str1 = "]"; 
1850                                                 break;
1851                                         case ">" : 
1852                                                 $str1 = "<"; 
1853                                                 break;
1854                                         case "<" : 
1855                                                 $str1 = ">"; 
1856                                                 break;
1857                                 }
1858                                 if (in_array($str1, $number))
1859                                 {
1860                                         $num .= $str1;
1861                                         $str1 = "";
1862                                 }
1863                                 if (!in_array($str_next, $number))
1864                                 {
1865                                         $str1 .= $num;
1866                                         $num = "";
1867                                 }
1868                                 $output = $str1.$output;
1869                         }
1870                         else
1871                         {
1872                                 if (($str1 == utf8_chr(0x060c)) || ($str1 == utf8_chr(0x061f)) || ($str1 == utf8_chr(0x0621)) || 
1873                                         (array_key_exists($str_next, $p_chars) && array_key_exists($str_back, $p_chars)) || 
1874                                         ($str1 == " " && array_key_exists($str_back, $p_chars)) || ($str1 == " " && 
1875                                         array_key_exists($str_next, $p_chars)))
1876                                 {
1877                                         if ($e_output)
1878                                         {
1879                                                 $output = $e_output.$output;
1880                                                 $e_output = "";
1881                                         }
1882                                         $output = $str1.$output;
1883                                 }
1884                                 else
1885                                 {
1886                                         $e_output .= $str1;
1887                                         if (array_key_exists($str_next, $p_chars) || $str_next == "")
1888                                         {
1889                                                 $output = $e_output.$output;
1890                                                 $e_output = "";
1891                                         }
1892                                 }
1893                         }
1894                 }
1895                 else
1896                         $output = $str1.$output;
1897                 $str_next = null;
1898                 $str_back = null;
1899         }
1900         return  $output;
1901 }
1902
1903 // For debugging
1904 if (!function_exists('dump'))
1905 {
1906         function dump($str)
1907         {
1908                 $req_dump = print_r($str, true);
1909                 $fp = fopen('dump.log', 'a');
1910                 date_default_timezone_set("Europe/Amsterdam");
1911                 $msg = "[" . date('Y-m-d / H:i:s') . "] - $req_dump\n";
1912                 fwrite($fp, $msg);
1913                 fclose($fp);
1914         }
1915 }