1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * Class for handling output in PDF format.
7 *
8 * Requires PHP extension PDFlib
9 *
10 * PHP versions 4 and 5
11 *
12 * LICENSE: This library is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or (at your
15 * option) any later version. This library is distributed in the hope that it
16 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
18 * General Public License for more details. You should have received a copy of
19 * the GNU Lesser General Public License along with this library; if not, write
20 * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 *
23 * @category   Images
24 * @package    Image_Canvas
25 * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
26 * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
27 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
28 * @version    CVS: $Id: PDF.php 287471 2009-08-18 23:12:01Z clockwerx $
29 * @link       http://pear.php.net/package/Image_Canvas
30 */
31
32/**
33 * Include file Image/Canvas.php
34 */
35require_once 'Image/Canvas.php';
36
37/**
38 * Include file Image/Canvas/Color.php
39 */
40require_once 'Image/Canvas/Color.php';
41
42/**
43 * PDF Canvas class.
44 *
45 * @category   Images
46 * @package    Image_Canvas
47 * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
48 * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
49 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
50 * @version    Release: @package_version@
51 * @link       http://pear.php.net/package/Image_Canvas
52 */
53class Image_Canvas_PDF extends Image_Canvas
54{
55
56    /**
57     * The PDF document
58     * @var resource
59     * @access private
60     */
61    var $_pdf;
62
63    /**
64     * The major version of PDFlib
65     * @var int
66     * @access private
67     */
68    var $_pdflib;
69
70    /**
71     * The font
72     * @var mixed
73     * @access private
74     */
75    var $_pdfFont = false;
76
77    /**
78     * The width of the page
79     * @var int
80     * @access private
81     */
82    var $_pageWidth;
83
84    /**
85     * The height of the page
86     * @var int
87     * @access private
88     */
89    var $_pageHeight;
90
91    /**
92     * Create the PDF canvas.
93     *
94     * Parameters available:
95     *
96     * 'page' Specify the page/paper format for the graph's page, available
97     * formats are: A0, A1, A2, A3, A4, A5, A6, B5, letter, legal, ledger,
98     * 11x17, cd_front, inlay, inlay_nosides
99     *
100     * 'align' Alignment of the graph on the page, available options are:
101     * topleft, topcenter, topright, leftcenter, center, rightcenter,
102     * leftbottom, centerbottom, rightbottom
103     *
104     * 'orientation' Specifies the paper orientation, default is 'portrait' and
105     * 'landscape' is also supported.
106     *
107     * 'creator' The creator tag of the PDF/graph
108     *
109     * 'author' The author tag of the PDF/graph
110     *
111     * 'title' The title tag of the PDF/graph
112     *
113     * 'width' The width of the graph on the page
114     *
115     * 'height' The height of the graph on the page
116     *
117     * 'left' The left offset of the graph on the page
118     *
119     * 'top' The top offset of the graph on the page
120     *
121     * 'filename' The PDF file to open/add page to, using 'filename' requires
122     * the commercial version of PDFlib (http://www.pdflib.com/), this has for
123     * obvious ($ 450) reasons not been tested
124     *
125     * 'pdf' An existing PDFlib PDF document to add the page to
126     *
127     * 'add_page' (true/false) Used together with 'pdf', to specify whether the
128     * canvas should add a new graph page (true) or create the graph on the
129     * current page (false), default is 'true'
130     *
131     * The 'page' and 'width' & 'height' can be mutually omitted, if 'page' is
132     * omitted the page is created using dimensions of width x height, and if
133     * width and height are omitted the page dimensions are used for the graph.
134     *
135     * If 'pdf' is specified, 'filename', 'creator', 'author' and 'title' has no
136     * effect.
137     *
138     * 'left' and 'top' are overridden by 'align'
139     *
140     * It is required either to specify 'width' & 'height' or 'page'.
141     *
142     * The PDF format/PDFlib has some limitations on the capabilities, which
143     * means some functionality available using other canvass (fx. alpha
144     * blending and gradient fills) are not supported with PDF (see Canvas.txt
145     * in the docs/ folder for further details)
146     *
147     * @param array $param Parameter array
148     */
149    function Image_Canvas_PDF($param)
150    {
151        if (isset($param['page'])) {
152            switch (strtoupper($param['page'])) {
153            case 'A0':
154                $this->_pageWidth = 2380;
155                $this->_pageHeight = 3368;
156                break;
157
158            case 'A1':
159                $this->_pageWidth = 1684;
160                $this->_pageHeight = 2380;
161                break;
162
163            case 'A2':
164                $this->_pageWidth = 1190;
165                $this->_pageHeight = 1684;
166                break;
167
168            case 'A3':
169                $this->_pageWidth = 842;
170                $this->_pageHeight = 1190;
171                break;
172
173            case 'A4':
174                $this->_pageWidth = 595;
175                $this->_pageHeight = 842;
176                break;
177
178            case 'A5':
179                $this->_pageWidth = 421;
180                $this->_pageHeight = 595;
181                break;
182
183            case 'A6':
184                $this->_pageWidth = 297;
185                $this->_pageHeight = 421;
186                break;
187
188            case 'B5':
189                $this->_pageWidth = 501;
190                $this->_pageHeight = 709;
191                break;
192
193            case 'LETTER':
194                $this->_pageWidth = 612;
195                $this->_pageHeight = 792;
196                break;
197
198            case 'LEGAL':
199                $this->_pageWidth = 612;
200                $this->_pageHeight = 1008;
201                break;
202
203            case 'LEDGER':
204                $this->_pageWidth = 1224;
205                $this->_pageHeight = 792;
206                break;
207
208            case '11X17':
209                $this->_pageWidth = 792;
210                $this->_pageHeight = 1224;
211                break;
212
213            case 'CD_FRONT':
214                $this->_pageWidth = 337;
215                $this->_pageHeight = 337;
216                break;
217
218            case 'INLAY':
219                $this->_pageWidth = 425;
220                $this->_pageHeight = 332;
221                break;
222
223            case 'INLAY_NOSIDES':
224                $this->_pageWidth = 390;
225                $this->_pageHeight = 332;
226                break;
227            }
228        }
229
230        if ((isset($param['orientation'])) && (strtoupper($param['orientation']) == 'LANDSCAPE')) {
231            $w = $this->_pageWidth;
232            $this->_pageWidth = $this->_pageHeight;
233            $this->_pageHeight = $w;
234        }
235
236        parent::Image_Canvas($param);
237
238        if (!$this->_pageWidth) {
239            $this->_pageWidth = $this->_width;
240        } elseif (!$this->_width) {
241            $this->_width = $this->_pageWidth;
242        }
243
244        if (!$this->_pageHeight) {
245            $this->_pageHeight = $this->_height;
246        } elseif (!$this->_height) {
247            $this->_height = $this->_pageHeight;
248        }
249
250        $this->_width = min($this->_width, $this->_pageWidth);
251        $this->_height = min($this->_height, $this->_pageHeight);
252
253        if ((isset($param['align'])) &&
254            (($this->_width != $this->_pageWidth) || ($this->_height != $this->_pageHeight))
255        ) {
256            switch (strtoupper($param['align'])) {
257            case 'TOPLEFT':
258                $this->_top = 0;
259                $this->_left = 0;
260                break;
261
262            case 'TOPCENTER':
263                $this->_top = 0;
264                $this->_left = ($this->_pageWidth - $this->_width) / 2;
265                break;
266
267            case 'TOPRIGHT':
268                $this->_top = 0;
269                $this->_left = $this->_pageWidth - $this->_width;
270                break;
271
272            case 'LEFTCENTER':
273                $this->_top = ($this->_pageHeight - $this->_height) / 2;
274                $this->_left = 0;
275                break;
276
277            case 'CENTER':
278                $this->_top = ($this->_pageHeight - $this->_height) / 2;
279                $this->_left = ($this->_pageWidth - $this->_width) / 2;
280                break;
281
282            case 'RIGHTCENTER':
283                $this->_top = ($this->_pageHeight - $this->_height) / 2;
284                $this->_left = $this->_pageWidth - $this->_width;
285                break;
286
287            case 'LEFTBOTTOM':
288                $this->_top = $this->_pageHeight - $this->_height;
289                $this->_left = 0;
290                break;
291
292            case 'CENTERBOTTOM':
293                $this->_top = $this->_pageHeight - $this->_height;
294                $this->_left = ($this->_pageWidth - $this->_width) / 2;
295                break;
296
297            case 'RIGHTBOTTOM':
298                $this->_top = $this->_pageHeight - $this->_height;
299                $this->_left = $this->_pageWidth - $this->_width;
300                break;
301            }
302        }
303
304        $addPage = true;
305        if ((isset($param['pdf'])) && (is_resource($param['pdf']))) {
306            $this->_pdf =& $param['pdf'];
307            if ((isset($param['add_page'])) && ($param['add_page'] === false)) {
308                $addPage = false;
309            }
310        } else {
311            $this->_pdf = pdf_new();
312
313            if (isset($param['filename'])) {
314                pdf_open_file($this->_pdf, $param['filename']);
315            } else {
316                pdf_open_file($this->_pdf, '');
317            }
318
319            pdf_set_parameter($this->_pdf, 'warning', 'true');
320
321            pdf_set_info($this->_pdf, 'Creator', (isset($param['creator']) ? $param['creator'] : 'PEAR::Image_Canvas'));
322            pdf_set_info($this->_pdf, 'Author', (isset($param['author']) ? $param['author'] : 'Jesper Veggerby'));
323            pdf_set_info($this->_pdf, 'Title', (isset($param['title']) ? $param['title'] : 'Image_Canvas'));
324        }
325
326        if ($addPage) {
327            pdf_begin_page($this->_pdf, $this->_pageWidth, $this->_pageHeight);
328        }
329        $this->_reset();
330
331        $this->_pdflib = $this->_version();
332    }
333
334    /**
335     * Get the x-point from the relative to absolute coordinates
336     *
337     * @param float $x The relative x-coordinate (in percentage of total width)
338     * @return float The x-coordinate as applied to the canvas
339     * @access private
340     */
341    function _getX($x)
342    {
343        return $this->_left + $x;
344    }
345
346    /**
347     * Get the y-point from the relative to absolute coordinates
348     *
349     * @param float $y The relative y-coordinate (in percentage of total width)
350     * @return float The y-coordinate as applied to the canvas
351     * @access private
352     */
353    function _getY($y)
354    {
355        return $this->_pageHeight - ($this->_top + $y);
356    }
357
358    /**
359     * Get the color index for the RGB color
360     *
361     * @param int $color The color
362     * @return int The GD image index of the color
363     * @access private
364     */
365    function _color($color = false)
366    {
367        if (($color === false) || ($color === 'opague') || ($color === 'transparent')) {
368            return false;
369        } else {
370            $color = Image_Canvas_Color::color2RGB($color);
371            $color[0] = $color[0]/255;
372            $color[1] = $color[1]/255;
373            $color[2] = $color[2]/255;
374            return $color;
375        }
376    }
377
378    /**
379     * Get the PDF linestyle
380     *
381     * @param mixed $lineStyle The line style to return, false if the one
382     *   explicitly set
383     * @return bool True if set (so that a line should be drawn)
384     * @access private
385     */
386    function _setLineStyle($lineStyle = false)
387    {
388        if ($lineStyle === false) {
389            $lineStyle = $this->_lineStyle;
390        }
391
392        if (($lineStyle == 'transparent') || ($lineStyle === false)) {
393            return false;
394        }
395
396        if (is_array($lineStyle)) {
397            // TODO Implement linestyles in PDFlib (using pdf_setcolor(.., 'pattern'...); ?
398            reset($lineStyle);
399            $lineStyle = current($lineStyle);
400        }
401
402        $color = $this->_color($lineStyle);
403
404        pdf_setlinewidth($this->_pdf, $this->_thickness);
405        if ($this->_pdflib < 4) {
406            pdf_setrgbcolor_stroke($this->_pdf, $color[0]/255, $color[1]/255, $color[2]/255);
407        } else {
408            pdf_setcolor($this->_pdf, 'stroke', 'rgb', $color[0], $color[1], $color[2], 0);
409        }
410        return true;
411    }
412
413    /**
414     * Set the PDF fill style
415     *
416     * @param mixed $fillStyle The fillstyle to return, false if the one
417     *   explicitly set
418     * @return bool True if set (so that a line should be drawn)
419     * @access private
420     */
421    function _setFillStyle($fillStyle = false)
422    {
423        if ($fillStyle === false) {
424            $fillStyle = $this->_fillStyle;
425        }
426
427        if (($fillStyle == 'transparent') || ($fillStyle === false)) {
428            return false;
429        }
430
431        $color = $this->_color($fillStyle);
432
433        if ($this->_pdflib < 4) {
434            pdf_setrgbcolor_fill($this->_pdf, $color[0]/255, $color[1]/255, $color[2]/255);
435        } else {
436            pdf_setcolor($this->_pdf, 'fill', 'rgb', $color[0], $color[1], $color[2], 0);
437        }
438        return true;
439    }
440
441    /**
442     * Set the PDF font
443     *
444     * @access private
445     */
446    function _setFont()
447    {
448        $this->_pdfFont = false;
449        if (isset($this->_font['name'])) {
450            pdf_set_parameter($this->_pdf, 'FontOutline', $this->_font['name'] . '=' . $this->_font['file']);
451            $this->_pdfFont = pdf_findfont($this->_pdf, $this->_font['name'], $this->_font['encoding'], 1);
452
453            if ($this->_pdfFont) {
454                pdf_setfont($this->_pdf, $this->_pdfFont, $this->_font['size']);
455                $this->_setFillStyle($this->_font['color']);
456            }
457        } else {
458            $this->_setFillStyle('black');
459        }
460    }
461
462    /**
463     * Sets an image that should be used for filling.
464     *
465     * Image filling is not supported with PDF, filling 'transparent'
466     *
467     * @param string $filename The filename of the image to fill with
468     */
469    function setFillImage($filename)
470    {
471        $this->_fillStyle = 'transparent';
472    }
473
474    /**
475     * Sets a gradient fill
476     *
477     * Gradient filling is not supported with PDF, end color used as solid fill.
478     *
479     * @param array $gradient Gradient fill options
480     */
481    function setGradientFill($gradient)
482    {
483        $this->_fillStyle = $gradient['end'];
484    }
485
486    /**
487     * Sets the font options.
488     *
489     * The $font array may have the following entries:
490     *
491     * 'ttf' = the .ttf file (either the basename, filename or full path)
492     * If 'ttf' is specified, then the following can be specified
493     *
494     * 'size' = size in pixels
495     *
496     * 'angle' = the angle with which to write the text
497     *
498     * @param array $font The font options.
499     */
500    function setFont($fontOptions)
501    {
502        parent::setFont($fontOptions);
503
504        if (!isset($this->_font['size'])) {
505            $this->_font['size'] = 12;
506        }
507
508        if (!isset($this->_font['encoding'])) {
509            $this->_font['encoding'] = 'winansi';
510        }
511
512        if (!isset($this->_font['color'])) {
513            $this->_font['color'] = 'black';
514        }
515    }
516
517    /**
518     * Resets the canvas.
519     *
520     * Includes fillstyle, linestyle, thickness and polygon
521     *
522     * @access private
523     */
524    function _reset()
525    {
526        pdf_initgraphics($this->_pdf);
527        parent::_reset();
528    }
529
530    /**
531     * Draw a line
532     *
533     * Parameter array:
534     * 'x0': int X start point
535     * 'y0': int Y start point
536     * 'x1': int X end point
537     * 'y1': int Y end point
538     * 'color': mixed [optional] The line color
539     * @param array $params Parameter array
540     */
541    function line($params)
542    {
543        $color = (isset($params['color']) ? $params['color'] : false);
544        if ($this->_setLineStyle($color)) {
545            pdf_moveto($this->_pdf, $this->_getX($params['x0']), $this->_getY($params['y0']));
546            pdf_lineto($this->_pdf, $this->_getX($params['x1']), $this->_getY($params['y1']));
547            pdf_stroke($this->_pdf);
548        }
549        parent::line($params);
550    }
551
552    /**
553     * Parameter array:
554     * 'connect': bool [optional] Specifies whether the start point should be
555     *   connected to the endpoint (closed polygon) or not (connected line)
556     * 'fill': mixed [optional] The fill color
557     * 'line': mixed [optional] The line color
558     * @param array $params Parameter array
559     */
560    function polygon($params = array())
561    {
562        $connectEnds = (isset($params['connect']) ? $params['connect'] : false);
563        $fillColor = (isset($params['fill']) ? $params['fill'] : false);
564        $lineColor = (isset($params['line']) ? $params['line'] : false);
565
566        $line = $this->_setLineStyle($lineColor);
567        $fill = false;
568        if ($connectEnds) {
569            $fill = $this->_setFillStyle($fillColor);
570        }
571
572        $first = true;
573        foreach ($this->_polygon as $point) {
574            if ($first === true) {
575                pdf_moveto($this->_pdf, $point['X'], $point['Y']);
576                $first = $point;
577            } else {
578                if (isset($last['P1X'])) {
579                    pdf_curveto($this->_pdf,
580                        $last['P1X'],
581                        $last['P1Y'],
582                        $last['P2X'],
583                        $last['P2Y'],
584                        $point['X'],
585                        $point['Y']
586                    );
587                } else {
588                    pdf_lineto($this->_pdf,
589                        $point['X'],
590                        $point['Y']
591                    );
592                }
593            }
594            $last = $point;
595        }
596
597        if ($connectEnds) {
598            if (isset($last['P1X'])) {
599                pdf_curveto($this->_pdf,
600                    $last['P1X'],
601                    $last['P1Y'],
602                    $last['P2X'],
603                    $last['P2Y'],
604                    $first['X'],
605                    $first['Y']
606                );
607            } else {
608                pdf_lineto($this->_pdf,
609                    $first['X'],
610                    $first['Y']
611                );
612            }
613        }
614
615        if (($line) && ($fill)) {
616            pdf_fill_stroke($this->_pdf);
617        } elseif ($line) {
618            pdf_stroke($this->_pdf);
619        } elseif ($fill) {
620            pdf_fill($this->_pdf);
621        }
622        parent::polygon($params);
623    }
624
625    /**
626     * Draw a rectangle
627     *
628     * Parameter array:
629     * 'x0': int X start point
630     * 'y0': int Y start point
631     * 'x1': int X end point
632     * 'y1': int Y end point
633     * 'fill': mixed [optional] The fill color
634     * 'line': mixed [optional] The line color
635     * @param array $params Parameter array
636     */
637    function rectangle($params)
638    {
639        $x0 = $this->_getX($params['x0']);
640        $y0 = $this->_getY($params['y0']);
641        $x1 = $this->_getX($params['x1']);
642        $y1 = $this->_getY($params['y1']);
643        $fillColor = (isset($params['fill']) ? $params['fill'] : false);
644        $lineColor = (isset($params['line']) ? $params['line'] : false);
645
646        $line = $this->_setLineStyle($lineColor);
647        $fill = $this->_setFillStyle($fillColor);
648        if (($line) || ($fill)) {
649            pdf_rect($this->_pdf, min($x0, $x1), min($y0, $y1), abs($x1 - $x0), abs($y1 - $y0));
650            if (($line) && ($fill)) {
651                pdf_fill_stroke($this->_pdf);
652            } elseif ($line) {
653                pdf_stroke($this->_pdf);
654            } elseif ($fill) {
655                pdf_fill($this->_pdf);
656            }
657        }
658        parent::rectangle($params);
659    }
660
661    /**
662     * Draw an ellipse
663     *
664     * Parameter array:
665     * 'x': int X center point
666     * 'y': int Y center point
667     * 'rx': int X radius
668     * 'ry': int Y radius
669     * 'fill': mixed [optional] The fill color
670     * 'line': mixed [optional] The line color
671     * @param array $params Parameter array
672     */
673    function ellipse($params)
674    {
675        $x = $params['x'];
676        $y = $params['y'];
677        $rx = $params['rx'];
678        $ry = $params['ry'];
679        $fillColor = (isset($params['fill']) ? $params['fill'] : false);
680        $lineColor = (isset($params['line']) ? $params['line'] : false);
681
682        $line = $this->_setLineStyle($lineColor);
683        $fill = $this->_setFillStyle($fillColor);
684        if (($line) || ($fill)) {
685            if ($rx == $ry) {
686                pdf_circle($this->_pdf, $this->_getX($x), $this->_getY($y), $rx);
687            } else {
688                pdf_moveto($this->_pdf, $this->_getX($x - $rx), $this->_getY($y));
689                pdf_curveto($this->_pdf,
690                    $this->_getX($x - $rx), $this->_getY($y),
691                    $this->_getX($x - $rx), $this->_getY($y - $ry),
692                    $this->_getX($x), $this->_getY($y - $ry)
693                );
694                pdf_curveto($this->_pdf,
695                    $this->_getX($x), $this->_getY($y - $ry),
696                    $this->_getX($x + $rx), $this->_getY($y - $ry),
697                    $this->_getX($x + $rx), $this->_getY($y)
698                );
699                pdf_curveto($this->_pdf,
700                    $this->_getX($x + $rx), $this->_getY($y),
701                    $this->_getX($x + $rx), $this->_getY($y + $ry),
702                    $this->_getX($x), $this->_getY($y + $ry)
703                );
704                pdf_curveto($this->_pdf,
705                    $this->_getX($x), $this->_getY($y + $ry),
706                    $this->_getX($x - $rx), $this->_getY($y + $ry),
707                    $this->_getX($x - $rx), $this->_getY($y)
708                );
709            }
710
711            if (($line) && ($fill)) {
712                pdf_fill_stroke($this->_pdf);
713            } elseif ($line) {
714                pdf_stroke($this->_pdf);
715            } elseif ($fill) {
716                pdf_fill($this->_pdf);
717            }
718        }
719        parent::ellipse($params);
720    }
721
722    /**
723     * Draw a pie slice
724     *
725     * Parameter array:
726     * 'x': int X center point
727     * 'y': int Y center point
728     * 'rx': int X radius
729     * 'ry': int Y radius
730     * 'v1': int The starting angle (in degrees)
731     * 'v2': int The end angle (in degrees)
732     * 'srx': int [optional] Starting X-radius of the pie slice (i.e. for a doughnut)
733     * 'sry': int [optional] Starting Y-radius of the pie slice (i.e. for a doughnut)
734     * 'fill': mixed [optional] The fill color
735     * 'line': mixed [optional] The line color
736     * @param array $params Parameter array
737     */
738    function pieslice($params)
739    {
740        $x = $this->_getX($params['x']);
741        $y = $this->_getY($params['y']);
742        $rx = $this->_getX($params['rx']);
743        $ry = $this->_getY($params['ry']);
744        $v1 = $this->_getX($params['v1']);
745        $v2 = $this->_getY($params['v2']);
746        $srx = $this->_getX($params['srx']);
747        $sry = $this->_getY($params['sry']);
748        $fillColor = (isset($params['fill']) ? $params['fill'] : false);
749        $lineColor = (isset($params['line']) ? $params['line'] : false);
750
751        // TODO Implement PDFLIB::pieSlice()
752        parent::pieslice($params);
753    }
754
755    /**
756     * Get the width of a text,
757     *
758     * @param string $text The text to get the width of
759     * @return int The width of the text
760     */
761    function textWidth($text)
762    {
763        if ($this->_pdfFont === false) {
764             return $this->_font['size'] * 0.7 * strlen($text);
765         } else {
766            return pdf_stringwidth($this->_pdf, $text, $this->_pdfFont, $this->_font['size']);
767        }
768    }
769
770    /**
771     * Get the height of a text,
772     *
773     * @param string $text The text to get the height of
774     * @return int The height of the text
775     */
776    function textHeight($text)
777    {
778        if (isset($this->_font['size'])) {
779            return $this->_font['size'];
780        } else {
781            return 12;
782        }
783    }
784
785    /**
786     * Writes text
787     *
788     * Parameter array:
789     * 'x': int X-point of text
790     * 'y': int Y-point of text
791     * 'text': string The text to add
792     * 'alignment': array [optional] Alignment
793     * 'color': mixed [optional] The color of the text
794     */
795    function addText($params)
796    {
797        $x = $this->_getX($params['x']);
798        $y = $this->_getY($params['y']);
799        $text = $params['text'];
800        $color = (isset($params['color']) ? $params['color'] : false);
801        $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
802
803        $this->_setFont();
804
805        $textWidth = $this->textWidth($text);
806        $textHeight = $this->textHeight($text);
807
808        if (!is_array($alignment)) {
809            $alignment = array('vertical' => 'top', 'horizontal' => 'left');
810        }
811
812        if (!isset($alignment['vertical'])) {
813            $alignment['vertical'] = 'top';
814        }
815
816        if (!isset($alignment['horizontal'])) {
817            $alignment['horizontal'] = 'left';
818        }
819
820        if ($alignment['horizontal'] == 'right') {
821            $x = $x - $textWidth;
822        } elseif ($alignment['horizontal'] == 'center') {
823            $x = $x - ($textWidth / 2);
824        }
825
826        $y -= $textHeight;
827
828        if ($alignment['vertical'] == 'bottom') {
829            $y = $y + $textHeight;
830        } elseif ($alignment['vertical'] == 'center') {
831            $y = $y + ($textHeight / 2);
832        }
833
834        if (($color === false) && (isset($this->_font['color']))) {
835            $color = $this->_font['color'];
836        }
837
838        pdf_show_xy($this->_pdf, $text, $x, $y);
839
840        parent::addText($params);
841    }
842
843    /**
844     * Overlay image
845     *
846     * Parameter array:
847     * 'x': int X-point of overlayed image
848     * 'y': int Y-point of overlayed image
849     * 'filename': string The filename of the image to overlay
850     * 'width': int [optional] The width of the overlayed image (resizing if possible)
851     * 'height': int [optional] The height of the overlayed image (resizing if possible)
852     * 'alignment': array [optional] Alignment
853     */
854    function image($params)
855    {
856        $x = $this->_getX($params['x']);
857        $y = $this->_getY($params['y']);
858        $filename = $params['filename'];
859        $width = (isset($params['width']) ? $params['width'] : false);
860        $height = (isset($params['height']) ? $params['height'] : false);
861        $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
862
863        if (substr($filename, -4) == '.png') {
864            $type = 'png';
865        } elseif (substr($filename, -4) == '.jpg') {
866            $type = 'jpeg';
867        }
868
869        $image = pdf_load_image($this->_pdf, $type, realpath($filename), '');
870        $width_ = pdf_get_value($this->_pdf, 'imagewidth', $image);
871        $height_ = pdf_get_value($this->_pdf, 'imageheight', $image);
872
873        $outputWidth = ($width !== false ? $width : $width_);
874        $outputHeight = ($height !== false ? $height : $height_);
875
876        if (!is_array($alignment)) {
877            $alignment = array('vertical' => 'top', 'horizontal' => 'left');
878        }
879
880        if (!isset($alignment['vertical'])) {
881            $alignment['vertical'] = 'top';
882        }
883
884        if (!isset($alignment['horizontal'])) {
885            $alignment['horizontal'] = 'left';
886        }
887
888        if ($alignment['horizontal'] == 'right') {
889            $x -= $outputWidth;
890        } elseif ($alignment['horizontal'] == 'center') {
891            $x -= $outputWidth / 2;
892        }
893
894        if ($alignment['vertical'] == 'top') {
895            $y += $outputHeight;
896        } elseif ($alignment['vertical'] == 'center') {
897            $y += $outputHeight / 2;
898        }
899
900        if (($width === false) && ($height === false)) {
901            $scale = 1;
902        } else {
903            $scale = max(($height/$height_), ($width/$width_));
904        }
905
906        pdf_place_image($this->_pdf, $image, $x, $y, $scale);
907        pdf_close_image($this->_pdf, $image);
908
909        parent::image($params);
910    }
911
912    /**
913     * Output the result of the canvas
914     *
915     * @param array $param Parameter array
916     * @abstract
917     */
918    function show($param = false)
919    {
920        parent::show($param);
921        pdf_end_page($this->_pdf);
922        pdf_close($this->_pdf);
923
924        $buf = pdf_get_buffer($this->_pdf);
925        $len = strlen($buf);
926
927        header('Content-type: application/pdf');
928        header('Content-Length: ' . $len);
929        header('Content-Disposition: inline; filename=image_graph.pdf');
930        print $buf;
931
932        pdf_delete($this->_pdf);
933    }
934
935    /**
936     * Output the result of the canvas
937     *
938     * @param array $param Parameter array
939     * @abstract
940     */
941    function save($param = false)
942    {
943        parent::save($param);
944        pdf_end_page($this->_pdf);
945        pdf_close($this->_pdf);
946
947        $buf = pdf_get_buffer($this->_pdf);
948        $len = strlen($buf);
949
950        $fp = @fopen($param['filename'], 'wb');
951        if ($fp) {
952            fwrite($fp, $buf, strlen($buf));
953            fclose($fp);
954        }
955        pdf_delete($this->_pdf);
956    }
957
958    /**
959     * Get a canvas specific HTML tag.
960     *
961     * This method implicitly saves the canvas to the filename in the
962     * filesystem path specified and parses it as URL specified by URL path
963     *
964     * Parameter array:
965     * 'filename': string
966     * 'filepath': string Path to the file on the file system. Remember the final slash
967     * 'urlpath': string Path to the file available through an URL. Remember the final slash
968     * 'title': string The url title
969     */
970    function toHtml($params)
971    {
972        parent::toHtml($params);
973        return '<a href="' . $params['urlpath'] . $params['filename'] . '">' . $params['title'] . '</a>';
974    }
975
976    /**
977     * Check which major version of PDFlib is installed
978     *
979     * @return int The mahor version number of PDFlib
980     * @access private
981     */
982    function _version()
983    {
984        $result = false;
985        $version = '';
986        if (function_exists('pdf_get_majorversion')) {
987            $version = pdf_get_majorversion();
988        } else if (function_exists('pdf_get_value')) {
989            $version = pdf_get_value($this->_pdf, 'major', 0);
990        } else {
991            ob_start();
992            phpinfo(8);
993            $php_info = ob_get_contents();
994            ob_end_clean();
995
996            if (preg_match("/<td[^>]*>PDFlib GmbH Version *<\/td><td[^>]*>([^<]*)<\/td>/",
997                $php_info, $result))
998            {
999                $version = $result[1];
1000            }
1001        }
1002
1003        if (preg_match('/([0-9]{1,2})\.[0-9]{1,2}(\.[0-9]{1,2})?/', trim($version), $result)) {
1004            return $result[1];
1005        } else {
1006            return $version;
1007        }
1008    }
1009
1010}
1011
1012?>
1013