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