Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * The File_PDF class provides a PHP-only implementation of a PDF library. No
4
 * external libs or PHP extensions are required.
5
 *
6
 * Based on the FPDF class by Olivier Plathey (http://www.fpdf.org/).
7
 *
8
 * $Horde: framework/File_PDF/PDF.php,v 1.18.10.22 2011/08/06 21:37:11 jan Exp $
9
 *
10
 * Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org>
11
 * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
12
 *
13
 * See the enclosed file COPYING for license information (LGPL). If you
14
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
15
 *
16
 * @author   Olivier Plathey <olivier@fpdf.org>
17
 * @author   Marko Djukic <marko@oblo.com>
18
 * @author   Jan Schneider <jan@horde.org>
19
 * @package  File_PDF
20
 * @category Fileformats
21
 */
22
 
23
/**
24
 * This hack works around Horde bug #4094
25
 * (http://bugs.horde.org/ticket/?id=4094)
26
 *
27
 * Once this package does not need to support PHP < 4.3.10 anymore the
28
 * following definiton can be removed and the ugly code can be removed
29
 * using
30
 *
31
 * sed -i -e 's/\' \. FILE_PDF_FLOAT \. \'/F/g' PDF.php
32
 */
33
if (version_compare(PHP_VERSION, '4.3.10', '>=')) {
34
    define('FILE_PDF_FLOAT', 'F');
35
} else {
36
    define('FILE_PDF_FLOAT', 'f');
37
}
38
 
39
class File_PDF {
40
 
41
    /**
42
     * Current page number.
43
     *
44
     * @var integer
45
     */
46
    var $_page = 0;
47
 
48
    /**
49
     * Current object number.
50
     *
51
     * @var integer
52
     */
53
    var $_n = 2;
54
 
55
    /**
56
     * Array of object offsets.
57
     *
58
     * @var array
59
     */
60
    var $_offsets = array();
61
 
62
    /**
63
     * Buffer holding in-memory PDF.
64
     *
65
     * @var string
66
     */
67
    var $_buffer = '';
68
 
69
    /**
70
     * Buffer length, including already flushed content.
71
     *
72
     * @var integer
73
     */
74
    var $_buflen = 0;
75
 
76
    /**
77
     * Whether the buffer has been flushed already.
78
     *
79
     * @var boolean
80
     */
81
    var $_flushed = false;
82
 
83
    /**
84
     * Array containing the pages.
85
     *
86
     * @var array
87
     */
88
    var $_pages = array();
89
 
90
    /**
91
     * Current document state.<pre>
92
     *   0 - initial state
93
     *   1 - document opened
94
     *   2 - page opened
95
     *   3 - document closed
96
     * </pre>
97
     *
98
     * @var integer
99
     */
100
    var $_state = 0;
101
 
102
    /**
103
     * Flag indicating if PDF file is to be compressed or not.
104
     *
105
     * @var boolean
106
     */
107
    var $_compress;
108
 
109
    /**
110
     * The default page orientation.
111
     *
112
     * @var string
113
     */
114
    var $_default_orientation;
115
 
116
    /**
117
     * The current page orientation.
118
     *
119
     * @var string
120
     */
121
    var $_current_orientation;
122
 
123
    /**
124
     * Array indicating orientation changes.
125
     *
126
     * @var array
127
     */
128
    var $_orientation_changes = array();
129
 
130
    /**
131
     * Current width of page format in points.
132
     *
133
     * @var float
134
     */
135
    var $fwPt;
136
 
137
    /**
138
     * Current height of page format in points.
139
     *
140
     * @var float
141
     */
142
    var $fhPt;
143
 
144
    /**
145
     * Current width of page format in user units.
146
     *
147
     * @var float
148
     */
149
    var $fw;
150
 
151
    /**
152
     * Current height of page format in user units.
153
     *
154
     * @var float
155
     */
156
    var $fh;
157
 
158
    /**
159
     * Current width of page in points.
160
     *
161
     * @var float
162
     */
163
    var $wPt;
164
 
165
    /**
166
     * Current height of page in points.
167
     *
168
     * @var float
169
     */
170
    var $hPt;
171
 
172
    /**
173
     * Current width of page in user units
174
     *
175
     * @var float
176
     */
177
    var $w;
178
 
179
    /**
180
     * Current height of page in user units
181
     *
182
     * @var float
183
     */
184
    var $h;
185
 
186
    /**
187
     * Scale factor (number of points in user units).
188
     *
189
     * @var float
190
     */
191
    var $_scale;
192
 
193
    /**
194
     * Left page margin size.
195
     *
196
     * @var float
197
     */
198
    var $_left_margin;
199
 
200
    /**
201
     * Top page margin size.
202
     *
203
     * @var float
204
     */
205
    var $_top_margin;
206
 
207
    /**
208
     * Right page margin size.
209
     *
210
     * @var float
211
     */
212
    var $_right_margin;
213
 
214
    /**
215
     * Break page margin size, the bottom margin which triggers a page break.
216
     *
217
     * @var float
218
     */
219
    var $_break_margin;
220
 
221
    /**
222
     * Cell margin size.
223
     *
224
     * @var float
225
     */
226
    var $_cell_margin;
227
 
228
    /**
229
     * The current horizontal position for cell positioning.
230
     * Value is set in user units and is calculated from the top left corner
231
     * as origin.
232
     *
233
     * @var float
234
     */
235
    var $x;
236
 
237
    /**
238
     * The current vertical position for cell positioning.
239
     * Value is set in user units and is calculated from the top left corner
240
     * as origin.
241
     *
242
     * @var float
243
     */
244
    var $y;
245
 
246
    /**
247
     * The height of the last cell printed.
248
     *
249
     * @var float
250
     */
251
    var $_last_height;
252
 
253
    /**
254
     * Line width in user units.
255
     *
256
     * @var float
257
     */
258
    var $_line_width;
259
 
260
    /**
261
     * An array of standard font names.
262
     *
263
     * @var array
264
     */
265
    var $_core_fonts = array('courier'      => 'Courier',
266
                             'courierB'     => 'Courier-Bold',
267
                             'courierI'     => 'Courier-Oblique',
268
                             'courierBI'    => 'Courier-BoldOblique',
269
                             'helvetica'    => 'Helvetica',
270
                             'helveticaB'   => 'Helvetica-Bold',
271
                             'helveticaI'   => 'Helvetica-Oblique',
272
                             'helveticaBI'  => 'Helvetica-BoldOblique',
273
                             'times'        => 'Times-Roman',
274
                             'timesB'       => 'Times-Bold',
275
                             'timesI'       => 'Times-Italic',
276
                             'timesBI'      => 'Times-BoldItalic',
277
                             'symbol'       => 'Symbol',
278
                             'zapfdingbats' => 'ZapfDingbats');
279
 
280
    /**
281
     * An array of used fonts.
282
     *
283
     * @var array
284
     */
285
    var $_fonts = array();
286
 
287
    /**
288
     * An array of font files.
289
     *
290
     * @var array
291
     */
292
    var $_font_files = array();
293
 
294
    /**
295
     * An array of encoding differences.
296
     *
297
     * @var array
298
     */
299
    var $_diffs = array();
300
 
301
    /**
302
     * An array of used images.
303
     *
304
     * @var array
305
     */
306
    var $_images = array();
307
 
308
    /**
309
     * An array of links in pages.
310
     *
311
     * @var array
312
     */
313
    var $_page_links;
314
 
315
    /**
316
     * An array of internal links.
317
     *
318
     * @var array
319
     */
320
    var $_links = array();
321
 
322
    /**
323
     * Current font family.
324
     *
325
     * @var string
326
     */
327
    var $_font_family = '';
328
 
329
    /**
330
     * Current font style.
331
     *
332
     * @var string
333
     */
334
    var $_font_style = '';
335
 
336
    /**
337
     * Underlining flag.
338
     *
339
     * @var boolean
340
     */
341
    var $_underline = false;
342
 
343
    /**
344
     * An array containing current font info.
345
     *
346
     * @var array
347
     */
348
    var $_current_font;
349
 
350
    /**
351
     * Current font size in points.
352
     *
353
     * @var float
354
     */
355
    var $_font_size_pt = 12;
356
 
357
    /**
358
     * Current font size in user units.
359
     *
360
     * @var float
361
     */
362
    var $_font_size = 12;
363
 
364
    /**
365
     * Commands for filling color.
366
     *
367
     * @var string
368
     */
369
    var $_fill_color = '0 g';
370
 
371
    /**
372
     * Commands for text color.
373
     *
374
     * @var string
375
     */
376
    var $_text_color = '0 g';
377
 
378
    /**
379
     * Whether text color is different from fill color.
380
     *
381
     * @var boolean
382
     */
383
    var $_color_flag = false;
384
 
385
    /**
386
     * Commands for drawing color.
387
     *
388
     * @var string
389
     */
390
    var $_draw_color = '0 G';
391
 
392
    /**
393
     * Word spacing.
394
     *
395
     * @var integer
396
     */
397
    var $_word_spacing = 0;
398
 
399
    /**
400
     * Automatic page breaking.
401
     *
402
     * @var boolean
403
     */
404
    var $_auto_page_break;
405
 
406
    /**
407
     * Threshold used to trigger page breaks.
408
     *
409
     * @var float
410
     */
411
    var $_page_break_trigger;
412
 
413
    /**
414
     * Flag set when processing footer.
415
     *
416
     * @var boolean
417
     */
418
    var $_in_footer = false;
419
 
420
    /**
421
     * Zoom display mode.
422
     *
423
     * @var string
424
     */
425
    var $_zoom_mode;
426
 
427
    /**
428
     * Layout display mode.
429
     *
430
     * @var string
431
     */
432
    var $_layout_mode;
433
 
434
    /**
435
     * An array containing the document info, consisting of:
436
     *   - title
437
     *   - subject
438
     *   - author
439
     *   - keywords
440
     *   - creator
441
     *
442
     * @var array
443
     */
444
    var $_info = array();
445
 
446
    /**
447
     * Alias for total number of pages.
448
     *
449
     * @var string
450
     */
451
    var $_alias_nb_pages = '{nb}';
452
 
453
    /**
454
     * Attempts to return a conrete PDF instance.
455
     *
456
     * It allows to set up the page format, the orientation and the units of
457
     * measurement used in all the methods (except for the font sizes).
458
     *
459
     * Example:
460
     * <code>
461
     * $pdf = File_PDF::factory(array('orientation' => 'P',
462
     *                                'unit' => 'mm',
463
     *                                'format' => 'A4'));
464
     * </code>
465
     *
466
     * @param array $params  A hash with parameters for the created PDF object.
467
     *                       Possible parameters are:
468
     *                       - orientation - Default page orientation. Possible
469
     *                         values are (case insensitive):
470
     *                         - P or Portrait (default)
471
     *                         - L or Landscape
472
     *                       - unit - User measure units. Possible values
473
     *                         values are:
474
     *                         - pt: point
475
     *                         - mm: millimeter (default)
476
     *                         - cm: centimeter
477
     *                         - in: inch
478
     *                         A point equals 1/72 of inch, that is to say
479
     *                         about 0.35 mm (an inch being 2.54 cm). This is a
480
     *                         very common unit in typography; font sizes are
481
     *                         expressed in that unit.
482
     *                       - format - The format used for pages. It can be
483
     *                         either one of the following values (case
484
     *                         insensitive):
485
     *                         - A3
486
     *                         - A4 (default)
487
     *                         - A5
488
     *                         - Letter
489
     *                         - Legal
490
     *                         or a custom format in the form of a two-element
491
     *                         array containing the width and the height
492
     *                         (expressed in the unit given by the unit
493
     *                         parameter).
494
     * @param string $class  The concrete class name to return an instance of.
495
     *                       Defaults to File_PDF.
496
     */
497
    function &factory($params = array(), $class = 'File_PDF')
498
    {
499
        /* Default parameters. */
500
        $defaults = array('orientation' => 'P',
501
                          'unit' => 'mm',
502
                          'format' => 'A4');
503
 
504
        /* Backward compatibility with old method signature. */
505
        /* Should be removed a few versions later. */
506
        if (!is_array($params)) {
507
            $class = 'File_PDF';
508
            $params = $defaults;
509
            $names = array_keys($defaults);
510
            for ($i = 0; $i < func_num_args(); $i++) {
511
                $params[$names[$i]] = func_get_arg($i);
512
            }
513
        } else {
514
            $params = array_merge($defaults, $params);
515
        }
516
 
517
        /* Create the PDF object. */
518
        $pdf = new $class($params);
519
 
520
        /* Scale factor. */
521
        if ($params['unit'] == 'pt') {
522
            $pdf->_scale = 1;
523
        } elseif ($params['unit'] == 'mm') {
524
            $pdf->_scale = 72 / 25.4;
525
        } elseif ($params['unit'] == 'cm') {
526
            $pdf->_scale = 72 / 2.54;
527
        } elseif ($params['unit'] == 'in') {
528
            $pdf->_scale = 72;
529
        } else {
530
            $error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit']));
531
            return $error;
532
        }
533
        /* Page format. */
534
        if (is_string($params['format'])) {
535
            $params['format'] = strtolower($params['format']);
536
            if ($params['format'] == 'a3') {
537
                $params['format'] = array(841.89, 1190.55);
538
            } elseif ($params['format'] == 'a4') {
539
                $params['format'] = array(595.28, 841.89);
540
            } elseif ($params['format'] == 'a5') {
541
                $params['format'] = array(420.94, 595.28);
542
            } elseif ($params['format'] == 'letter') {
543
                $params['format'] = array(612, 792);
544
            } elseif ($params['format'] == 'legal') {
545
                $params['format'] = array(612, 1008);
546
            } else {
547
                $error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format']));
548
                return $error;
549
            }
550
            $pdf->fwPt = $params['format'][0];
551
            $pdf->fhPt = $params['format'][1];
552
        } else {
553
            $pdf->fwPt = $params['format'][0] * $pdf->_scale;
554
            $pdf->fhPt = $params['format'][1] * $pdf->_scale;
555
        }
556
        $pdf->fw = $pdf->fwPt / $pdf->_scale;
557
        $pdf->fh = $pdf->fhPt / $pdf->_scale;
558
 
559
        /* Page orientation. */
560
        $params['orientation'] = strtolower($params['orientation']);
561
        if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') {
562
            $pdf->_default_orientation = 'P';
563
            $pdf->wPt = $pdf->fwPt;
564
            $pdf->hPt = $pdf->fhPt;
565
        } elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') {
566
            $pdf->_default_orientation = 'L';
567
            $pdf->wPt = $pdf->fhPt;
568
            $pdf->hPt = $pdf->fwPt;
569
        } else {
570
            $error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation']));
571
            return $error;
572
        }
573
        $pdf->_current_orientation = $pdf->_default_orientation;
574
        $pdf->w = $pdf->wPt / $pdf->_scale;
575
        $pdf->h = $pdf->hPt / $pdf->_scale;
576
 
577
        /* Page margins (1 cm) */
578
        $margin = 28.35 / $pdf->_scale;
579
        $pdf->setMargins($margin, $margin);
580
 
581
        /* Interior cell margin (1 mm) */
582
        $pdf->_cell_margin = $margin / 10;
583
 
584
        /* Line width (0.2 mm) */
585
        $pdf->_line_width = .567 / $pdf->_scale;
586
 
587
        /* Automatic page break */
588
        $pdf->setAutoPageBreak(true, 2 * $margin);
589
 
590
        /* Full width display mode */
591
        $pdf->setDisplayMode('fullwidth');
592
 
593
        /* Compression */
594
        $pdf->setCompression(true);
595
 
596
        return $pdf;
597
    }
598
 
599
    /**
600
     * Returns a PEAR_Error object.
601
     *
602
     * Wraps around PEAR::raiseError() to avoid having to include PEAR.php
603
     * unless an error occurs.
604
     *
605
     * @param mixed $error  The error message.
606
     *
607
     * @return object PEAR_Error
608
     */
609
    function raiseError($error)
610
    {
611
        require_once 'PEAR.php';
612
        return PEAR::raiseError($error);
613
    }
614
 
615
    /**
616
     * Defines the left, top and right margins.
617
     *
618
     * By default, they equal 1 cm. Call this method to change them.
619
     *
620
     * @param float $left   Left margin.
621
     * @param float $top    Top margin.
622
     * @param float $right  Right margin. If not specified default to the value
623
     *                      of the left one.
624
     *
625
     * @see setAutoPageBreak()
626
     * @see setLeftMargin()
627
     * @see setRightMargin()
628
     * @see setTopMargin()
629
     */
630
    function setMargins($left, $top, $right = null)
631
    {
632
        /* Set left and top margins. */
633
        $this->_left_margin  = $left;
634
        $this->_top_margin   = $top;
635
        /* If no right margin set default to same as left. */
636
        $this->_right_margin = (is_null($right) ? $left : $right);
637
    }
638
 
639
    /**
640
     * Defines the left margin.
641
     *
642
     * The method can be called before creating the first page.  If the
643
     * current abscissa gets out of page, it is brought back to the margin.
644
     *
645
     * @param float $margin  The margin.
646
     *
647
     * @see setAutoPageBreak()
648
     * @see setMargins()
649
     * @see setRightMargin()
650
     * @see setTopMargin()
651
     */
652
    function setLeftMargin($margin)
653
    {
654
        $this->_left_margin = $margin;
655
        /* If there is a current page and the current X position is less than
656
         * margin set the X position to the margin value. */
657
        if ($this->_page > 0 && $this->x < $margin) {
658
            $this->x = $margin;
659
        }
660
    }
661
 
662
    /**
663
     * Defines the top margin.
664
     *
665
     * The method can be called before creating the first page.
666
     *
667
     * @param float $margin  The margin.
668
     */
669
    function setTopMargin($margin)
670
    {
671
        $this->_top_margin = $margin;
672
    }
673
 
674
    /**
675
     * Defines the right margin.
676
     *
677
     * The method can be called before creating the first page.
678
     *
679
     * @param float $margin  The margin.
680
     */
681
    function setRightMargin($margin)
682
    {
683
        $this->_right_margin = $margin;
684
    }
685
 
686
    /**
687
     * Returns the actual page width.
688
     *
689
     * @since File_PDF 0.2.0
690
     * @since Horde 3.2
691
     *
692
     * @return float  The page width.
693
     */
694
    function getPageWidth()
695
    {
696
        return ($this->w - $this->_right_margin - $this->_left_margin);
697
    }
698
 
699
    /**
700
     * Returns the actual page height.
701
     *
702
     * @since File_PDF 0.2.0
703
     * @since Horde 3.2
704
     *
705
     * @return float  The page height.
706
     */
707
    function getPageHeight()
708
    {
709
        return ($this->h - $this->_top_margin - $this->_break_margin);
710
    }
711
 
712
    /**
713
     * Enables or disables the automatic page breaking mode.
714
     *
715
     * When enabling, the second parameter is the distance from the bottom of
716
     * the page that defines the triggering limit. By default, the mode is on
717
     * and the margin is 2 cm.
718
     *
719
     * @param boolean $auto  Boolean indicating if mode should be on or off.
720
     * @param float $margin  Distance from the bottom of the page.
721
     */
722
    function setAutoPageBreak($auto, $margin = 0)
723
    {
724
        $this->_auto_page_break    = $auto;
725
        $this->_break_margin       = $margin;
726
        $this->_page_break_trigger = $this->h - $margin;
727
    }
728
 
729
    /**
730
     * Defines the way the document is to be displayed by the viewer.
731
     *
732
     * The zoom level can be set: pages can be displayed entirely on screen,
733
     * occupy the full width of the window, use real size, be scaled by a
734
     * specific zooming factor or use viewer default (configured in the
735
     * Preferences menu of Acrobat). The page layout can be specified too:
736
     * single at once, continuous display, two columns or viewer default.  By
737
     * default, documents use the full width mode with continuous display.
738
     *
739
     * @param mixed $zoom    The zoom to use. It can be one of the following
740
     *                       string values:
741
     *                         - fullpage: entire page on screen
742
     *                         - fullwidth: maximum width of window
743
     *                         - real: uses real size (100% zoom)
744
     *                         - default: uses viewer default mode
745
     *                       or a number indicating the zooming factor.
746
     * @param string layout  The page layout. Possible values are:
747
     *                         - single: one page at once
748
     *                         - continuous: pages in continuously
749
     *                         - two: two pages on two columns
750
     *                         - default: uses viewer default mode
751
     *                       Default value is continuous.
752
     */
753
    function setDisplayMode($zoom, $layout = 'continuous')
754
    {
755
        $zoom = strtolower($zoom);
756
        if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real'
757
            || $zoom == 'default' || !is_string($zoom)) {
758
            $this->_zoom_mode = $zoom;
759
        } elseif ($zoom == 'zoom') {
760
            $this->_zoom_mode = $layout;
761
        } else {
762
            return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom));
763
        }
764
 
765
        $layout = strtolower($layout);
766
        if ($layout == 'single' || $layout == 'continuous' || $layout == 'two'
767
            || $layout == 'default') {
768
            $this->_layout_mode = $layout;
769
        } elseif ($zoom != 'zoom') {
770
            return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout));
771
        }
772
    }
773
 
774
    /**
775
     * Activates or deactivates page compression.
776
     *
777
     * When activated, the internal representation of each page is compressed,
778
     * which leads to a compression ratio of about 2 for the resulting
779
     * document. Compression is on by default.
780
     *
781
     * Note: the {@link http://www.php.net/zlib/ zlib extension} is required
782
     * for this feature. If not present, compression will be turned off.
783
     *
784
     * @param boolean $compress  Boolean indicating if compression must be
785
     *                           enabled or not.
786
     */
787
    function setCompression($compress)
788
    {
789
        /* If no gzcompress function is available then default to false. */
790
        $this->_compress = (function_exists('gzcompress') ? $compress : false);
791
    }
792
 
793
    /**
794
     * Set the info to a document.
795
     *
796
     * Possible info settings are:
797
     *   - title
798
     *   - subject
799
     *   - author
800
     *   - keywords
801
     *   - creator
802
     *
803
     * @param array|string $info  If passed as an array then the complete hash
804
     *                            containing the info to be inserted into the
805
     *                            document. Otherwise the name of setting to be
806
     *                            set.
807
     * @param string $value       The value of the setting.
808
     */
809
    function setInfo($info, $value = '')
810
    {
811
        if (is_array($info)) {
812
            $this->_info = $info;
813
        } else {
814
            $this->_info[$info] = $value;
815
        }
816
    }
817
 
818
    /**
819
     * Defines an alias for the total number of pages.
820
     *
821
     * It will be substituted as the document is closed.
822
     *
823
     * Example:
824
     * <code>
825
     * class My_File_PDF extends File_PDF {
826
     *     function footer()
827
     *     {
828
     *         // Go to 1.5 cm from bottom
829
     *         $this->setY(-15);
830
     *         // Select Arial italic 8
831
     *         $this->setFont('Arial', 'I', 8);
832
     *         // Print current and total page numbers
833
     *         $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0,
834
     *                     0, 'C');
835
     *     }
836
     * }
837
     * $pdf = My_File_PDF::factory();
838
     * $pdf->aliasNbPages();
839
     * </code>
840
     *
841
     * @param string $alias  The alias.
842
     *
843
     * @see getPageNo()
844
     * @see footer()
845
     */
846
    function aliasNbPages($alias = '{nb}')
847
    {
848
        $this->_alias_nb_pages = $alias;
849
    }
850
 
851
    /**
852
     * This method begins the generation of the PDF document; it must be
853
     * called before any output commands.
854
     *
855
     * No page is created by this method, therefore it is necessary to call
856
     * {@link addPage()}.
857
     *
858
     * @see addPage()
859
     * @see close()
860
     */
861
    function open()
862
    {
863
        $this->_beginDoc();
864
    }
865
 
866
    /**
867
     * Terminates the PDF document. It is not necessary to call this method
868
     * explicitly because {@link output()} does it automatically.
869
     *
870
     * If the document contains no page, {@link addPage()} is called to
871
     * prevent from getting an invalid document.
872
     *
873
     * @see open()
874
     * @see output()
875
     */
876
    function close()
877
    {
878
        /* Terminate document */
879
        if ($this->_page == 0) {
880
            $result = $this->addPage();
881
            if (is_a($result, 'PEAR_Error')) {
882
                return $result;
883
            }
884
        }
885
        /* Page footer */
886
        $this->_in_footer = true;
887
        $this->x = $this->_left_margin;
888
        $this->footer();
889
        $this->_in_footer = false;
890
        /* Close page */
891
        $this->_endPage();
892
        /* Close document */
893
        $this->_endDoc();
894
    }
895
 
896
    /**
897
     * Adds a new page to the document.
898
     *
899
     * If a page is already present, the {@link footer()} method is called
900
     * first to output the footer. Then the page is added, the current
901
     * position set to the top-left corner according to the left and top
902
     * margins, and {@link header()} is called to display the header.
903
     *
904
     * The font which was set before calling is automatically restored. There
905
     * is no need to call {@link setFont()} again if you want to continue with
906
     * the same font. The same is true for colors and line width.  The origin
907
     * of the coordinate system is at the top-left corner and increasing
908
     * ordinates go downwards.
909
     *
910
     * @param string $orientation  Page orientation. Possible values
911
     *                             are (case insensitive):
912
     *                               - P or Portrait
913
     *                               - L or Landscape
914
     *                             The default value is the one passed to the
915
     *                             constructor.
916
     *
917
     * @see header()
918
     * @see footer()
919
     * @see setMargins()
920
     */
921
    function addPage($orientation = '')
922
    {
923
        /* For good measure make sure this is called. */
924
        $this->_beginDoc();
925
 
926
        /* Save style settings so that they are not overridden by footer() or
927
         * header(). */
928
        $lw = $this->_line_width;
929
        $dc = $this->_draw_color;
930
        $fc = $this->_fill_color;
931
        $tc = $this->_text_color;
932
        $cf = $this->_color_flag;
933
        $font_family = $this->_font_family;
934
        $font_style = $this->_font_style . ($this->_underline ? 'U' : '');
935
        $font_size  = $this->_font_size_pt;
936
 
937
        /* Close old page. */
938
        if ($this->_page > 0) {
939
            /* Page footer. */
940
            $this->_in_footer = true;
941
            $this->x = $this->_left_margin;
942
            $this->footer();
943
            $this->_in_footer = false;
944
 
945
            /* Close page. */
946
            $this->_endPage();
947
        }
948
 
949
        /* Start new page. */
950
        $this->_beginPage($orientation);
951
        /* Set line cap style to square. */
952
        $this->_out('2 J');
953
        /* Set line width. */
954
        $this->_line_width = $lw;
955
        $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
956
 
957
        /* Force the setting of the font. Each new page requires a new
958
         * call. */
959
        if ($font_family) {
960
            $result = $this->setFont($font_family, $font_style, $font_size, true);
961
            if (is_a($result, 'PEAR_Error')) {
962
                return $result;
963
            }
964
        }
965
 
966
        /* Restore styles. */
967
        if ($this->_fill_color != $fc) {
968
            $this->_fill_color = $fc;
969
            $this->_out($this->_fill_color);
970
        }
971
        if ($this->_draw_color != $dc) {
972
            $this->_draw_color = $dc;
973
            $this->_out($this->_draw_color);
974
        }
975
        $this->_text_color = $tc;
976
        $this->_color_flag = $cf;
977
 
978
        /* Page header. */
979
        $this->header();
980
 
981
        /* Restore styles. */
982
        if ($this->_line_width != $lw) {
983
            $this->_line_width = $lw;
984
            $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $lw * $this->_scale));
985
        }
986
        $result = $this->setFont($font_family, $font_style, $font_size);
987
        if (is_a($result, 'PEAR_Error')) {
988
            return $result;
989
        }
990
        if ($this->_fill_color != $fc) {
991
            $this->_fill_color = $fc;
992
            $this->_out($this->_fill_color);
993
        }
994
        if ($this->_draw_color != $dc) {
995
            $this->_draw_color = $dc;
996
            $this->_out($this->_draw_color);
997
        }
998
        $this->_text_color = $tc;
999
        $this->_color_flag = $cf;
1000
    }
1001
 
1002
    /**
1003
     * This method is used to render the page header.
1004
     *
1005
     * It is automatically called by {@link addPage()} and should not be
1006
     * called directly by the application. The implementation in File_PDF:: is
1007
     * empty, so you have to subclass it and override the method if you want a
1008
     * specific processing.
1009
     *
1010
     * Example:
1011
     * <code>
1012
     * class My_File_PDF extends File_PDF {
1013
     *     function header()
1014
     *     {
1015
     *         // Select Arial bold 15
1016
     *         $this->setFont('Arial', 'B', 15);
1017
     *         // Move to the right
1018
     *         $this->cell(80);
1019
     *         // Framed title
1020
     *         $this->cell(30, 10, 'Title', 1, 0, 'C');
1021
     *         // Line break
1022
     *         $this->newLine(20);
1023
     *     }
1024
     * }
1025
     * </code>
1026
     *
1027
     * @see footer()
1028
     */
1029
    function header()
1030
    {
1031
        /* To be implemented in your own inherited class. */
1032
    }
1033
 
1034
    /**
1035
     * This method is used to render the page footer.
1036
     *
1037
     * It is automatically called by {@link addPage()} and {@link close()} and
1038
     * should not be called directly by the application. The implementation in
1039
     * File_PDF:: is empty, so you have to subclass it and override the method
1040
     * if you want a specific processing.
1041
     *
1042
     * Example:
1043
     * <code>
1044
     * class My_File_PDF extends File_PDF {
1045
     *    function footer()
1046
     *    {
1047
     *        // Go to 1.5 cm from bottom
1048
     *        $this->setY(-15);
1049
     *        // Select Arial italic 8
1050
     *        $this->setFont('Arial', 'I', 8);
1051
     *        // Print centered page number
1052
     *        $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C');
1053
     *    }
1054
     * }
1055
     * </code>
1056
     *
1057
     * @see header()
1058
     */
1059
    function footer()
1060
    {
1061
        /* To be implemented in your own inherited class. */
1062
    }
1063
 
1064
    /**
1065
     * Returns the current page number.
1066
     *
1067
     * @return integer
1068
     *
1069
     * @see aliasNbPages()
1070
     */
1071
    function getPageNo()
1072
    {
1073
        return $this->_page;
1074
    }
1075
 
1076
    /**
1077
     * Sets the fill color.
1078
     *
1079
     * Depending on the colorspace called, the number of color component
1080
     * parameters required can be either 1, 3 or 4. The method can be called
1081
     * before the first page is created and the color is retained from page to
1082
     * page.
1083
     *
1084
     * @param string $cs  Indicates the colorspace which can be either 'rgb',
1085
     *                    'cmyk' or 'gray'. Defaults to 'rgb'.
1086
     * @param float $c1   First color component, floating point value between 0
1087
     *                    and 1. Required for gray, rgb and cmyk.
1088
     * @param float $c2   Second color component, floating point value
1089
     *                    between 0 and 1. Required for rgb and cmyk.
1090
     * @param float $c3   Third color component, floating point value between 0
1091
     *                    and 1. Required for rgb and cmyk.
1092
     * @param float $c4   Fourth color component, floating point value
1093
     *                    between 0 and 1. Required for cmyk.
1094
     *
1095
     * @see setTextColor()
1096
     * @see setDrawColor()
1097
     * @see rect()
1098
     * @see cell()
1099
     * @see multiCell()
1100
     */
1101
    function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
1102
    {
1103
        $cs = strtolower($cs);
1104
        if ($cs == 'rgb') {
1105
            $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
1106
        } elseif ($cs == 'cmyk') {
1107
            $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' k', $c1, $c2, $c3, $c4);
1108
        } else {
1109
            $this->_fill_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
1110
        }
1111
        if ($this->_page > 0) {
1112
            $this->_out($this->_fill_color);
1113
        }
1114
        $this->_color_flag = $this->_fill_color != $this->_text_color;
1115
    }
1116
 
1117
    /**
1118
     * Sets the text color.
1119
     *
1120
     * Depending on the colorspace called, the number of color component
1121
     * parameters required can be either 1, 3 or 4. The method can be called
1122
     * before the first page is created and the color is retained from page to
1123
     * page.
1124
     *
1125
     * @param string $cs  Indicates the colorspace which can be either 'rgb',
1126
     *                    'cmyk' or 'gray'. Defaults to 'rgb'.
1127
     * @param float $c1   First color component, floating point value between 0
1128
     *                    and 1. Required for gray, rgb and cmyk.
1129
     * @param float $c2   Second color component, floating point value
1130
     *                    between 0 and 1. Required for rgb and cmyk.
1131
     * @param float $c3   Third color component, floating point value between 0
1132
     *                    and 1. Required for rgb and cmyk.
1133
     * @param float $c4   Fourth color component, floating point value
1134
     *                    between 0 and 1. Required for cmyk.
1135
     *
1136
     * @since File_PDF 0.2.0
1137
     * @since Horde 3.2
1138
     * @see setFillColor()
1139
     * @see setDrawColor()
1140
     * @see rect()
1141
     * @see cell()
1142
     * @see multiCell()
1143
     */
1144
    function setTextColor($cs, $c1, $c2 = 0, $c3 = 0, $c4 = 0)
1145
    {
1146
        $cs = strtolower($cs);
1147
        if ($cs == 'rgb') {
1148
            $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' rg', $c1, $c2, $c3);
1149
        } elseif ($cs == 'cmyk') {
1150
            $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' k', $c1, $c2, $c3, $c4);
1151
        } else {
1152
            $this->_text_color = sprintf('%.3' . FILE_PDF_FLOAT . ' g', $c1);
1153
        }
1154
        $this->_color_flag = $this->_fill_color != $this->_text_color;
1155
    }
1156
 
1157
    /**
1158
     * Sets the draw color, used when drawing lines.
1159
     *
1160
     * Depending on the colorspace called, the number of color component
1161
     * parameters required can be either 1, 3 or 4. The method can be called
1162
     * before the first page is created and the color is retained from page to
1163
     * page.
1164
     *
1165
     * @param string $cs  Indicates the colorspace which can be either 'rgb',
1166
     *                    'cmyk' or 'gray'. Defaults to 'rgb'.
1167
     * @param float $c1   First color component, floating point value between 0
1168
     *                    and 1. Required for gray, rgb and cmyk.
1169
     * @param float $c2   Second color component, floating point value
1170
     *                    between 0 and 1. Required for rgb and cmyk.
1171
     * @param float $c3   Third color component, floating point value between 0
1172
     *                    and 1. Required for rgb and cmyk.
1173
     * @param float $c4   Fourth color component, floating point value
1174
     *                    between 0 and 1. Required for cmyk.
1175
     *
1176
     * @see setFillColor()
1177
     * @see line()
1178
     * @see rect()
1179
     * @see cell()
1180
     * @see multiCell()
1181
     */
1182
    function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
1183
    {
1184
        $cs = strtolower($cs);
1185
        if ($cs == 'rgb') {
1186
            $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' RG', $c1, $c2, $c3);
1187
        } elseif ($cs == 'cmyk') {
1188
            $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' %.3' . FILE_PDF_FLOAT . ' K', $c1, $c2, $c3, $c4);
1189
        } else {
1190
            $this->_draw_color = sprintf('%.3' . FILE_PDF_FLOAT . ' G', $c1);
1191
        }
1192
        if ($this->_page > 0) {
1193
            $this->_out($this->_draw_color);
1194
        }
1195
    }
1196
 
1197
    /**
1198
     * Returns the length of a text string. A font must be selected.
1199
     *
1200
     * @param string $text  The text whose length is to be computed.
1201
     * @param boolean $pt   Whether the width should be returned in points or
1202
     *                      user units.
1203
     *
1204
     * @return float
1205
     */
1206
    function getStringWidth($text, $pt = false)
1207
    {
1208
        $text = (string)$text;
1209
        $width = 0;
1210
        $length = strlen($text);
1211
        for ($i = 0; $i < $length; $i++) {
1212
            $width += $this->_current_font['cw'][$text{$i}];
1213
        }
1214
 
1215
        /* Adjust for word spacing. */
1216
        $width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' '];
1217
 
1218
        if ($pt) {
1219
            return $width * $this->_font_size_pt / 1000;
1220
        } else {
1221
            return $width * $this->_font_size / 1000;
1222
        }
1223
    }
1224
 
1225
    /**
1226
     * Defines the line width.
1227
     *
1228
     * By default, the value equals 0.2 mm. The method can be called before
1229
     * the first page is created and the value is retained from page to page.
1230
     *
1231
     * @param float $width  The width.
1232
     *
1233
     * @see line()
1234
     * @see rect()
1235
     * @see cell()
1236
     * @see multiCell()
1237
     */
1238
    function setLineWidth($width)
1239
    {
1240
        $this->_line_width = $width;
1241
        if ($this->_page > 0) {
1242
            $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' w', $width * $this->_scale));
1243
        }
1244
    }
1245
 
1246
    /**
1247
     * Draws a line between two points.
1248
     *
1249
     * All coordinates can be negative to provide values from the right or
1250
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
1251
     *
1252
     * @param float $x1  Abscissa of first point.
1253
     * @param float $y1  Ordinate of first point.
1254
     * @param float $x2  Abscissa of second point.
1255
     * @param float $y2  Ordinate of second point.
1256
     *
1257
     * @see setLineWidth()
1258
     * @see setDrawColor()
1259
     */
1260
    function line($x1, $y1, $x2, $y2)
1261
    {
1262
        if ($x1 < 0) {
1263
            $x1 += $this->w;
1264
        }
1265
        if ($y1 < 0) {
1266
            $y1 += $this->h;
1267
        }
1268
        if ($x2 < 0) {
1269
            $x2 += $this->w;
1270
        }
1271
        if ($y2 < 0) {
1272
            $y2 += $this->h;
1273
        }
1274
 
1275
        $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S', $x1 * $this->_scale, ($this->h - $y1) * $this->_scale, $x2 * $this->_scale, ($this->h - $y2) * $this->_scale));
1276
    }
1277
 
1278
    /**
1279
     * Outputs a rectangle.
1280
     *
1281
     * It can be drawn (border only), filled (with no border) or both.
1282
     *
1283
     * All coordinates can be negative to provide values from the right or
1284
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
1285
     *
1286
     * @param float $x       Abscissa of upper-left corner.
1287
     * @param float $y       Ordinate of upper-left corner.
1288
     * @param float $width   Width.
1289
     * @param float $height  Height.
1290
     * @param float $style   Style of rendering. Possible values are:
1291
     *                         - D or empty string: draw (default)
1292
     *                         - F: fill
1293
     *                         - DF or FD: draw and fill
1294
     *
1295
     * @see setLineWidth()
1296
     * @see setDrawColor()
1297
     * @see setFillColor()
1298
     */
1299
    function rect($x, $y, $width, $height, $style = '')
1300
    {
1301
        if ($x < 0) {
1302
            $x += $this->w;
1303
        }
1304
        if ($y < 0) {
1305
            $y += $this->h;
1306
        }
1307
 
1308
        $style = strtoupper($style);
1309
        if ($style == 'F') {
1310
            $op = 'f';
1311
        } elseif ($style == 'FD' || $style == 'DF') {
1312
            $op = 'B';
1313
        } else {
1314
            $op = 'S';
1315
        }
1316
 
1317
        $x      = $this->_toPt($x);
1318
        $y      = $this->_toPt($y);
1319
        $width  = $this->_toPt($width);
1320
        $height = $this->_toPt($height);
1321
 
1322
        $this->_out(sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re %s', $x, $this->hPt - $y, $width, -$height, $op));
1323
    }
1324
 
1325
    /**
1326
     * Outputs a circle. It can be drawn (border only), filled (with no
1327
     * border) or both.
1328
     *
1329
     * All coordinates can be negative to provide values from the right or
1330
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
1331
     *
1332
     * @param float $x       Abscissa of the center of the circle.
1333
     * @param float $y       Ordinate of the center of the circle.
1334
     * @param float $r       Circle radius.
1335
     * @param string $style  Style of rendering. Possible values are:
1336
     *                         - D or empty string: draw (default)
1337
     *                         - F: fill
1338
     *                         - DF or FD: draw and fill
1339
     */
1340
    function circle($x, $y, $r, $style = '')
1341
    {
1342
        if ($x < 0) {
1343
            $x += $this->w;
1344
        }
1345
        if ($y < 0) {
1346
            $y += $this->h;
1347
        }
1348
 
1349
        $style = strtolower($style);
1350
        if ($style == 'f') {
1351
            $op = 'f';      // Style is fill only.
1352
        } elseif ($style == 'fd' || $style == 'df') {
1353
            $op = 'B';      // Style is fill and stroke.
1354
        } else {
1355
            $op = 'S';      // Style is stroke only.
1356
        }
1357
 
1358
        $x = $this->_toPt($x);
1359
        $y = $this->_toPt($y);
1360
        $r = $this->_toPt($r);
1361
 
1362
        /* Invert the y scale. */
1363
        $y = $this->hPt - $y;
1364
        /* Length of the Bezier control. */
1365
        $b = $r * 0.552;
1366
 
1367
        /* Move from the given origin and set the current point
1368
         * to the start of the first Bezier curve. */
1369
        $c = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m', $x - $r, $y);
1370
        $x = $x - $r;
1371
        /* First circle quarter. */
1372
        $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
1373
                      $x, $y + $b,           // First control point.
1374
                      $x + $r - $b, $y + $r, // Second control point.
1375
                      $x + $r, $y + $r);     // Final point.
1376
        /* Set x/y to the final point. */
1377
        $x = $x + $r;
1378
        $y = $y + $r;
1379
        /* Second circle quarter. */
1380
        $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
1381
                      $x + $b, $y,
1382
                      $x + $r, $y - $r + $b,
1383
                      $x + $r, $y - $r);
1384
        /* Set x/y to the final point. */
1385
        $x = $x + $r;
1386
        $y = $y - $r;
1387
        /* Third circle quarter. */
1388
        $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c',
1389
                      $x, $y - $b,
1390
                      $x - $r + $b, $y - $r,
1391
                      $x - $r, $y - $r);
1392
        /* Set x/y to the final point. */
1393
        $x = $x - $r;
1394
        $y = $y - $r;
1395
        /* Fourth circle quarter. */
1396
        $c .= sprintf(' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' c %s',
1397
                      $x - $b, $y,
1398
                      $x - $r, $y + $r - $b,
1399
                      $x - $r, $y + $r,
1400
                      $op);
1401
        /* Output the whole string. */
1402
        $this->_out($c);
1403
    }
1404
 
1405
    /**
1406
     * Imports a TrueType or Type1 font and makes it available. It is
1407
     * necessary to generate a font definition file first with the
1408
     * makefont.php utility.
1409
     * The location of the definition file (and the font file itself when
1410
     * embedding) must be found at the full path name included.
1411
     *
1412
     * Example:
1413
     * <code>
1414
     * $pdf->addFont('Comic', 'I');
1415
     * is equivalent to:
1416
     * $pdf->addFont('Comic', 'I', 'comici.php');
1417
     * </code>
1418
     *
1419
     * @param string $family  Font family. The name can be chosen arbitrarily.
1420
     *                        If it is a standard family name, it will
1421
     *                        override the corresponding font.
1422
     * @param string $style   Font style. Possible values are (case
1423
     *                        insensitive):
1424
     *                          - empty string: regular (default)
1425
     *                          - B: bold
1426
     *                          - I: italic
1427
     *                          - BI or IB: bold italic
1428
     * @param string $file    The font definition file. By default, the name is
1429
     *                        built from the family and style, in lower case
1430
     *                        with no space.
1431
     *
1432
     * @see setFont()
1433
     */
1434
    function addFont($family, $style = '', $file = '')
1435
    {
1436
        $family = strtolower($family);
1437
        if ($family == 'arial') {
1438
            $family = 'helvetica';
1439
        }
1440
 
1441
        $style = strtoupper($style);
1442
        if ($style == 'IB') {
1443
            $style = 'BI';
1444
        }
1445
        if (isset($this->_fonts[$family . $style])) {
1446
            return $this->raiseError(sprintf('Font already added: %s %s', $family, $style));
1447
        }
1448
        if ($file == '') {
1449
            $file = str_replace(' ', '', $family) . strtolower($style) . '.php';
1450
        }
1451
        include($file);
1452
        if (!isset($name)) {
1453
            return $this->raiseError('Could not include font definition file');
1454
        }
1455
        $i = count($this->_fonts) + 1;
1456
        $this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
1457
        if ($diff) {
1458
            /* Search existing encodings. */
1459
            $d = 0;
1460
            $nb = count($this->_diffs);
1461
            for ($i = 1; $i <= $nb; $i++) {
1462
                if ($this->_diffs[$i] == $diff) {
1463
                    $d = $i;
1464
                    break;
1465
                }
1466
            }
1467
            if ($d == 0) {
1468
                $d = $nb + 1;
1469
                $this->_diffs[$d] = $diff;
1470
            }
1471
            $this->_fonts[$family . $style]['diff'] = $d;
1472
        }
1473
        if ($file) {
1474
            if ($type == 'TrueType') {
1475
                $this->_font_files[$file] = array('length1' => $originalsize);
1476
            } else {
1477
                $this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2);
1478
            }
1479
        }
1480
    }
1481
 
1482
    /**
1483
     * Sets the font used to print character strings.
1484
     *
1485
     * It is mandatory to call this method at least once before printing text
1486
     * or the resulting document would not be valid. The font can be either a
1487
     * standard one or a font added via the {@link addFont()} method. Standard
1488
     * fonts use Windows encoding cp1252 (Western Europe).
1489
     *
1490
     * The method can be called before the first page is created and the font
1491
     * is retained from page to page.
1492
     *
1493
     * If you just wish to change the current font size, it is simpler to call
1494
     * {@link setFontSize()}.
1495
     *
1496
     * @param string $family  Family font. It can be either a name defined by
1497
     *                        {@link addFont()} or one of the standard families
1498
     *                        (case insensitive):
1499
     *                          - Courier (fixed-width)
1500
     *                          - Helvetica or Arial (sans serif)
1501
     *                          - Times (serif)
1502
     *                          - Symbol (symbolic)
1503
     *                          - ZapfDingbats (symbolic)
1504
     *                        It is also possible to pass an empty string. In
1505
     *                        that case, the current family is retained.
1506
     * @param string $style   Font style. Possible values are (case
1507
     *                        insensitive):
1508
     *                          - empty string: regular
1509
     *                          - B: bold
1510
     *                          - I: italic
1511
     *                          - U: underline
1512
     *                        or any combination. Bold and italic styles do not
1513
     *                        apply to Symbol and ZapfDingbats.
1514
     * @param integer $size   Font size in points. The default value is the
1515
     *                        current size. If no size has been specified since
1516
     *                        the beginning of the document, the value taken
1517
     *                        is 12.
1518
     * @param boolean $force  Force the setting of the font. Each new page will
1519
     *                        require a new call to {@link setFont()} and
1520
     *                        settings this to true will make sure that the
1521
     *                        checks for same font calls will be skipped.
1522
     *
1523
     * @see addFont()
1524
     * @see setFontSize()
1525
     * @see cell()
1526
     * @see multiCell()
1527
     * @see write()
1528
     */
1529
    function setFont($family, $style = '', $size = null, $force = false)
1530
    {
1531
        $family = strtolower($family);
1532
        if (empty($family)) {
1533
            $family = $this->_font_family;
1534
        }
1535
        if ($family == 'arial') {
1536
            /* Use helvetica instead of arial. */
1537
            $family = 'helvetica';
1538
        } elseif ($family == 'symbol' || $family == 'zapfdingbats') {
1539
            /* These two fonts do not have styles available. */
1540
            $style = '';
1541
        }
1542
 
1543
        $style = strtoupper($style);
1544
 
1545
        /* Underline is handled separately, if specified in the style var
1546
         * remove it from the style and set the underline flag. */
1547
        if (strpos($style, 'U') !== false) {
1548
            $this->_underline = true;
1549
            $style = str_replace('U', '', $style);
1550
        } else {
1551
            $this->_underline = false;
1552
        }
1553
 
1554
        if ($style == 'IB') {
1555
            $style = 'BI';
1556
        }
1557
 
1558
        /* If no size specified, use current size. */
1559
        if (is_null($size)) {
1560
            $size = $this->_font_size_pt;
1561
        }
1562
 
1563
        /* If font requested is already the current font and no force setting
1564
         * of the font is requested (eg. when adding a new page) don't bother
1565
         * with the rest of the function and simply return. */
1566
        if ($this->_font_family == $family && $this->_font_style == $style &&
1567
            $this->_font_size_pt == $size && !$force) {
1568
            return;
1569
        }
1570
 
1571
        /* Set the font key. */
1572
        $fontkey = $family . $style;
1573
 
1574
        /* Test if already cached. */
1575
        if (!isset($this->_fonts[$fontkey])) {
1576
            /* Get the character width definition file. */
1577
            $font_widths = File_PDF::_getFontFile($fontkey);
1578
            if (is_a($font_widths, 'PEAR_Error')) {
1579
                return $font_widths;
1580
            }
1581
 
1582
            $i = count($this->_fonts) + 1;
1583
            $this->_fonts[$fontkey] = array(
1584
                'i'    => $i,
1585
                'type' => 'core',
1586
                'name' => $this->_core_fonts[$fontkey],
1587
                'up'   => -100,
1588
                'ut'   => 50,
1589
                'cw'   => $font_widths[$fontkey]);
1590
        }
1591
 
1592
        /* Store font information as current font. */
1593
        $this->_font_family  = $family;
1594
        $this->_font_style   = $style;
1595
        $this->_font_size_pt = $size;
1596
        $this->_font_size    = $size / $this->_scale;
1597
        $this->_current_font = &$this->_fonts[$fontkey];
1598
 
1599
        /* Output font information if at least one page has been defined. */
1600
        if ($this->_page > 0) {
1601
            $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
1602
        }
1603
    }
1604
 
1605
    /**
1606
     * Defines the size of the current font.
1607
     *
1608
     * @param float $size  The size (in points).
1609
     *
1610
     * @see setFont()
1611
     */
1612
    function setFontSize($size)
1613
    {
1614
        /* If the font size is already the current font size, just return. */
1615
        if ($this->_font_size_pt == $size) {
1616
            return;
1617
        }
1618
        /* Set the current font size, both in points and scaled to user
1619
         * units. */
1620
        $this->_font_size_pt = $size;
1621
        $this->_font_size = $size / $this->_scale;
1622
 
1623
        /* Output font information if at least one page has been defined. */
1624
        if ($this->_page > 0) {
1625
            $this->_out(sprintf('BT /F%d %.2' . FILE_PDF_FLOAT . ' Tf ET', $this->_current_font['i'], $this->_font_size_pt));
1626
        }
1627
    }
1628
 
1629
    /**
1630
     * Defines the style of the current font.
1631
     *
1632
     * @param string $style  The font style.
1633
     *
1634
     * @since File_PDF 0.2.0
1635
     * @since Horde 3.2
1636
     * @see setFont()
1637
     */
1638
    function setFontStyle($style)
1639
    {
1640
        return $this->setFont($this->_font_family, $style);
1641
    }
1642
 
1643
    /**
1644
     * Creates a new internal link and returns its identifier.
1645
     *
1646
     * An internal link is a clickable area which directs to another place
1647
     * within the document.
1648
     *
1649
     * The identifier can then be passed to {@link cell()}, {@link()} write,
1650
     * {@link image()} or {@link link()}. The destination is defined with
1651
     * {@link setLink()}.
1652
     *
1653
     * @see cell()
1654
     * @see write()
1655
     * @see image()
1656
     * @see link()
1657
     * @see setLink()
1658
     */
1659
    function addLink()
1660
    {
1661
        $n = count($this->_links) + 1;
1662
        $this->_links[$n] = array(0, 0);
1663
        return $n;
1664
    }
1665
 
1666
    /**
1667
     * Defines the page and position a link points to.
1668
     *
1669
     * @param integer $link  The link identifier returned by {@link addLink()}.
1670
     * @param float $y       Ordinate of target position; -1 indicates the
1671
     *                       current position. The default value is 0 (top of
1672
     *                       page).
1673
     * @param integer $page  Number of target page; -1 indicates the current
1674
     *                       page.
1675
     *
1676
     * @see addLink()
1677
     */
1678
    function setLink($link, $y = 0, $page = -1)
1679
    {
1680
        if ($y == -1) {
1681
            $y = $this->y;
1682
        }
1683
        if ($page == -1) {
1684
            $page = $this->_page;
1685
        }
1686
        $this->_links[$link] = array($page, $y);
1687
    }
1688
 
1689
    /**
1690
     * Puts a link on a rectangular area of the page.
1691
     *
1692
     * Text or image links are generally put via {@link cell()}, {@link
1693
     * write()} or {@link image()}, but this method can be useful for instance
1694
     * to define a clickable area inside an image.
1695
     *
1696
     * All coordinates can be negative to provide values from the right or
1697
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
1698
     *
1699
     * @param float $x       Abscissa of the upper-left corner of the
1700
     *                       rectangle.
1701
     * @param float $y       Ordinate of the upper-left corner of the
1702
     *                       rectangle.
1703
     * @param float $width   Width of the rectangle.
1704
     * @param float $height  Height of the rectangle.
1705
     * @param mixed $link    URL or identifier returned by {@link addLink()}.
1706
     *
1707
     * @see addLink()
1708
     * @see cell()
1709
     * @see write()
1710
     * @see image()
1711
     */
1712
    function link($x, $y, $width, $height, $link)
1713
    {
1714
        if ($x < 0) {
1715
            $x += $this->w;
1716
        }
1717
        if ($y < 0) {
1718
            $y += $this->h;
1719
        }
1720
 
1721
        /* Set up the coordinates with correct scaling in pt. */
1722
        $x      = $this->_toPt($x);
1723
        $y      = $this->hPt - $this->_toPt($y);
1724
        $width  = $this->_toPt($width);
1725
        $height = $this->_toPt($height);
1726
 
1727
        /* Save link to page links array. */
1728
        $this->_link($x, $y, $width, $height, $link);
1729
    }
1730
 
1731
    /**
1732
     * Prints a character string.
1733
     *
1734
     * The origin is on the left of the first character, on the baseline. This
1735
     * method allows to place a string precisely on the page, but it is
1736
     * usually easier to use {@link cell()}, {@link multiCell()} or {@link
1737
     * write()} which are the standard methods to print text.
1738
     *
1739
     * All coordinates can be negative to provide values from the right or
1740
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
1741
     *
1742
     * @param float $x      Abscissa of the origin.
1743
     * @param float $y      Ordinate of the origin.
1744
     * @param string $text  String to print.
1745
     *
1746
     * @see setFont()
1747
     * @see cell()
1748
     * @see multiCell()
1749
     * @see write()
1750
     */
1751
    function text($x, $y, $text)
1752
    {
1753
        if ($x < 0) {
1754
            $x += $this->w;
1755
        }
1756
        if ($y < 0) {
1757
            $y += $this->h;
1758
        }
1759
 
1760
        /* Scale coordinates into points and set correct Y position. */
1761
        $x = $this->_toPt($x);
1762
        $y = $this->hPt - $this->_toPt($y);
1763
 
1764
        /* Escape any potentially harmful characters. */
1765
        $text = $this->_escape($text);
1766
 
1767
        $out = sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
1768
        if ($this->_underline && $text != '') {
1769
            $out .= ' ' . $this->_doUnderline($x, $y, $text);
1770
        }
1771
        if ($this->_color_flag) {
1772
            $out = sprintf('q %s %s Q', $this->_text_color, $out);
1773
        }
1774
        $this->_out($out);
1775
    }
1776
 
1777
    /**
1778
     * Whenever a page break condition is met, the method is called, and the
1779
     * break is issued or not depending on the returned value. The default
1780
     * implementation returns a value according to the mode selected by
1781
     * {@link setAutoPageBreak()}.
1782
     * This method is called automatically and should not be called directly
1783
     * by the application.
1784
     *
1785
     * @return boolean
1786
     *
1787
     * @see setAutoPageBreak()
1788
     */
1789
    function acceptPageBreak()
1790
    {
1791
        return $this->_auto_page_break;
1792
    }
1793
 
1794
    /**
1795
     * Prints a cell (rectangular area) with optional borders, background
1796
     * color and character string.
1797
     *
1798
     * The upper-left corner of the cell corresponds to the current
1799
     * position. The text can be aligned or centered. After the call, the
1800
     * current position moves to the right or to the next line. It is possible
1801
     * to put a link on the text.  If automatic page breaking is enabled and
1802
     * the cell goes beyond the limit, a page break is done before outputting.
1803
     *
1804
     * @param float $width   Cell width. If 0, the cell extends up to the right
1805
     *                       margin.
1806
     * @param float $height  Cell height.
1807
     * @param string $text   String to print.
1808
     * @param mixed $border  Indicates if borders must be drawn around the
1809
     *                       cell. The value can be either a number:
1810
     *                         - 0: no border (default)
1811
     *                         - 1: frame
1812
     *                       or a string containing some or all of the
1813
     *                       following characters (in any order):
1814
     *                         - L: left
1815
     *                         - T: top
1816
     *                         - R: right
1817
     *                         - B: bottom
1818
     * @param integer $ln    Indicates where the current position should go
1819
     *                       after the call. Possible values are:
1820
     *                         - 0: to the right (default)
1821
     *                         - 1: to the beginning of the next line
1822
     *                         - 2: below
1823
     *                       Putting 1 is equivalent to putting 0 and calling
1824
     *                       {@link newLine()} just after.
1825
     * @param string $align  Allows to center or align the text. Possible
1826
     *                       values are:
1827
     *                         - L or empty string: left (default)
1828
     *                         - C: center
1829
     *                         - R: right
1830
     * @param integer $fill  Indicates if the cell fill type. Possible values
1831
     *                       are:
1832
     *                         - 0: transparent (default)
1833
     *                         - 1: painted
1834
     * @param string $link   URL or identifier returned by {@link addLink()}.
1835
     *
1836
     * @see setFont()
1837
     * @see setDrawColor()
1838
     * @see setFillColor()
1839
     * @see setLineWidth()
1840
     * @see addLink()
1841
     * @see newLine()
1842
     * @see multiCell()
1843
     * @see write()
1844
     * @see setAutoPageBreak()
1845
     */
1846
    function cell($width, $height = 0, $text = '', $border = 0, $ln = 0,
1847
                  $align = '', $fill = 0, $link = '')
1848
    {
1849
        $k = $this->_scale;
1850
        if ($this->y + $height > $this->_page_break_trigger &&
1851
            !$this->_in_footer && $this->acceptPageBreak()) {
1852
            $x = $this->x;
1853
            $ws = $this->_word_spacing;
1854
            if ($ws > 0) {
1855
                $this->_word_spacing = 0;
1856
                $this->_out('0 Tw');
1857
            }
1858
            $result = $this->addPage($this->_current_orientation);
1859
            if (is_a($result, 'PEAR_Error')) {
1860
                return $result;
1861
            }
1862
            $this->x = $x;
1863
            if ($ws > 0) {
1864
                $this->_word_spacing = $ws;
1865
                $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw', $ws * $k));
1866
            }
1867
        }
1868
        if ($width == 0) {
1869
            $width = $this->w - $this->_right_margin - $this->x;
1870
        }
1871
        $s = '';
1872
        if ($fill == 1 || $border == 1) {
1873
            if ($fill == 1) {
1874
                $op = ($border == 1) ? 'B' : 'f';
1875
            } else {
1876
                $op = 'S';
1877
            }
1878
            $s = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re %s ', $this->x * $k, ($this->h - $this->y) * $k, $width * $k, -$height * $k, $op);
1879
        }
1880
        if (is_string($border)) {
1881
            if (strpos($border, 'L') !== false) {
1882
                $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - $this->y) * $k, $this->x * $k, ($this->h - ($this->y + $height)) * $k);
1883
            }
1884
            if (strpos($border, 'T') !== false) {
1885
                $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - $this->y) * $k);
1886
            }
1887
            if (strpos($border, 'R') !== false) {
1888
                $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', ($this->x + $width) * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
1889
            }
1890
            if (strpos($border, 'B') !== false) {
1891
                $s .= sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' m %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' l S ', $this->x * $k, ($this->h - ($this->y + $height)) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
1892
            }
1893
        }
1894
        if ($text != '') {
1895
            if ($align == 'R') {
1896
                $dx = $width - $this->_cell_margin - $this->getStringWidth($text);
1897
            } elseif ($align == 'C') {
1898
                $dx = ($width - $this->getStringWidth($text)) / 2;
1899
            } else {
1900
                $dx = $this->_cell_margin;
1901
            }
1902
            if ($this->_color_flag) {
1903
                $s .= 'q ' . $this->_text_color . ' ';
1904
            }
1905
            $text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text)));
1906
            $test2 = ((.5 * $height) + (.3 * $this->_font_size));
1907
            $test1 = $this->fhPt - (($this->y + $test2) * $k);
1908
            $x = ($this->x + $dx) * $k;
1909
            $y = ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k;
1910
            $s .= sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Td (%s) Tj ET', $x, $y, $text);
1911
            if ($this->_underline) {
1912
                $s .= ' ' . $this->_doUnderline($x, $y, $text);
1913
            }
1914
            if ($this->_color_flag) {
1915
                $s .= ' Q';
1916
            }
1917
            if ($link) {
1918
                $this->link($this->x + $dx, $this->y + .5 * $height- .5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link);
1919
            }
1920
        }
1921
        if ($s) {
1922
            $this->_out($s);
1923
        }
1924
        $this->_last_height = $height;
1925
        if ($ln > 0) {
1926
            /* Go to next line. */
1927
            $this->y += $height;
1928
            if ($ln == 1) {
1929
                $this->x = $this->_left_margin;
1930
            }
1931
        } else {
1932
            $this->x += $width;
1933
        }
1934
    }
1935
 
1936
    /**
1937
     * This method allows printing text with line breaks.
1938
     *
1939
     * They can be automatic (as soon as the text reaches the right border of
1940
     * the cell) or explicit (via the \n character). As many cells as
1941
     * necessary are output, one below the other. Text can be aligned,
1942
     * centered or justified. The cell block can be framed and the background
1943
     * painted.
1944
     *
1945
     * @param float $width   Width of cells. If 0, they extend up to the right
1946
     *                       margin of the page.
1947
     * @param float $height  Height of cells.
1948
     * @param string $text   String to print.
1949
     * @param mixed $border  Indicates if borders must be drawn around the cell
1950
     *                       block. The value can be either a number:
1951
     *                         - 0: no border (default)
1952
     *                         - 1: frame
1953
     *                       or a string containing some or all of the
1954
     *                       following characters (in any order):
1955
     *                         - L: left
1956
     *                         - T: top
1957
     *                         - R: right
1958
     *                         - B: bottom
1959
     * @param string $align  Sets the text alignment. Possible values are:
1960
     *                         - L: left alignment
1961
     *                         - C: center
1962
     *                         - R: right alignment
1963
     *                         - J: justification (default value)
1964
     * @param integer $fill  Indicates if the cell background must:
1965
     *                         - 0: transparent (default)
1966
     *                         - 1: painted
1967
     *
1968
     * @see setFont()
1969
     * @see setDrawColor()
1970
     * @see setFillColor()
1971
     * @see setLineWidth()
1972
     * @see cell()
1973
     * @see write()
1974
     * @see setAutoPageBreak()
1975
     */
1976
    function multiCell($width, $height, $text, $border = 0, $align = 'J',
1977
                       $fill = 0)
1978
    {
1979
        $cw = &$this->_current_font['cw'];
1980
        if ($width == 0) {
1981
            $width = $this->w - $this->_right_margin - $this->x;
1982
        }
1983
        $wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size;
1984
        $s = str_replace("\r", '', $text);
1985
        $nb = strlen($s);
1986
        if ($nb > 0 && $s[$nb-1] == "\n") {
1987
            $nb--;
1988
        }
1989
        $b = 0;
1990
        if ($border) {
1991
            if ($border == 1) {
1992
                $border = 'LTRB';
1993
                $b = 'LRT';
1994
                $b2 = 'LR';
1995
            } else {
1996
                $b2 = '';
1997
                if (strpos($border, 'L') !== false) {
1998
                    $b2 .= 'L';
1999
                }
2000
                if (strpos($border, 'R') !== false) {
2001
                    $b2 .= 'R';
2002
                }
2003
                $b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
2004
            }
2005
        }
2006
        $sep = -1;
2007
        $i   = 0;
2008
        $j   = 0;
2009
        $l   = 0;
2010
        $ns  = 0;
2011
        $nl  = 1;
2012
        while ($i < $nb) {
2013
            /* Get next character. */
2014
            $c = $s[$i];
2015
            if ($c == "\n") {
2016
                /* Explicit line break. */
2017
                if ($this->_word_spacing > 0) {
2018
                    $this->_word_spacing = 0;
2019
                    $this->_out('0 Tw');
2020
                }
2021
                $result = $this->cell($width, $height, substr($s, $j, $i-$j),
2022
                                      $b, 2, $align, $fill);
2023
                if (is_a($result, 'PEAR_Error')) {
2024
                    return $result;
2025
                }
2026
                $i++;
2027
                $sep = -1;
2028
                $j = $i;
2029
                $l = 0;
2030
                $ns = 0;
2031
                $nl++;
2032
                if ($border && $nl == 2) {
2033
                    $b = $b2;
2034
                }
2035
                continue;
2036
            }
2037
            if ($c == ' ') {
2038
                $sep = $i;
2039
                $ls = $l;
2040
                $ns++;
2041
            }
2042
            $l += $cw[$c];
2043
            if ($l > $wmax) {
2044
                /* Automatic line break. */
2045
                if ($sep == -1) {
2046
                    if ($i == $j) {
2047
                        $i++;
2048
                    }
2049
                    if ($this->_word_spacing > 0) {
2050
                        $this->_word_spacing = 0;
2051
                        $this->_out('0 Tw');
2052
                    }
2053
                    $result = $this->cell($width, $height,
2054
                                          substr($s, $j, $i - $j), $b, 2,
2055
                                          $align, $fill);
2056
                    if (is_a($result, 'PEAR_Error')) {
2057
                        return $result;
2058
                    }
2059
                } else {
2060
                    if ($align == 'J') {
2061
                        $this->_word_spacing = ($ns>1)
2062
                            ? ($wmax - $ls) / 1000 * $this->_font_size / ($ns - 1)
2063
                            : 0;
2064
                        $this->_out(sprintf('%.3' . FILE_PDF_FLOAT . ' Tw',
2065
                                            $this->_word_spacing * $this->_scale));
2066
                    }
2067
                    $result = $this->cell($width, $height,
2068
                                          substr($s, $j, $sep - $j),
2069
                                          $b, 2, $align, $fill);
2070
                    if (is_a($result, 'PEAR_Error')) {
2071
                        return $result;
2072
                    }
2073
                    $i = $sep + 1;
2074
                }
2075
                $sep = -1;
2076
                $j = $i;
2077
                $l = 0;
2078
                $ns = 0;
2079
                $nl++;
2080
                if ($border && $nl == 2) {
2081
                    $b = $b2;
2082
                }
2083
            } else {
2084
                $i++;
2085
            }
2086
        }
2087
        /* Last chunk. */
2088
        if ($this->_word_spacing > 0) {
2089
            $this->_word_spacing = 0;
2090
            $this->_out('0 Tw');
2091
        }
2092
        if ($border && strpos($border, 'B') !== false) {
2093
            $b .= 'B';
2094
        }
2095
        $result = $this->cell($width, $height, substr($s, $j, $i), $b, 2,
2096
                              $align, $fill);
2097
        if (is_a($result, 'PEAR_Error')) {
2098
            return $result;
2099
        }
2100
        $this->x = $this->_left_margin;
2101
    }
2102
 
2103
    /**
2104
     * This method prints text from the current position.
2105
     *
2106
     * When the right margin is reached (or the \n character is met) a line
2107
     * break occurs and text continues from the left margin. Upon method exit,
2108
     * the current position is left just at the end of the text.
2109
     *
2110
     * It is possible to put a link on the text.
2111
     *
2112
     * Example:
2113
     * <code>
2114
     * // Begin with regular font
2115
     * $pdf->setFont('Arial', '', 14);
2116
     * $pdf->write(5, 'Visit ');
2117
     * // Then put a blue underlined link
2118
     * $pdf->setTextColor(0, 0, 255);
2119
     * $pdf->setFont('', 'U');
2120
     * $pdf->write(5, 'www.fpdf.org', 'http://www.fpdf.org');
2121
     * </code>
2122
     *
2123
     * @param float $height  Line height.
2124
     * @param string $text   String to print.
2125
     * @param mixed $link    URL or identifier returned by {@link addLink()}.
2126
     *
2127
     * @see setFont()
2128
     * @see addLink()
2129
     * @see multiCell()
2130
     * @see setAutoPageBreak()
2131
     */
2132
    function write($height, $text, $link = '')
2133
    {
2134
        $cw = &$this->_current_font['cw'];
2135
        $width = $this->w - $this->_right_margin - $this->x;
2136
        $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
2137
        $s = str_replace("\r", '', $text);
2138
        $nb = strlen($s);
2139
        $sep = -1;
2140
        $i = 0;
2141
        $j = 0;
2142
        $l = 0;
2143
        $nl = 1;
2144
        while ($i < $nb) {
2145
            /* Get next character. */
2146
            $c = $s{$i};
2147
            if ($c == "\n") {
2148
                /* Explicit line break. */
2149
                $result = $this->cell($width, $height, substr($s, $j, $i - $j),
2150
                                      0, 2, '', 0, $link);
2151
                if (is_a($result, 'PEAR_Error')) {
2152
                    return $result;
2153
                }
2154
                $i++;
2155
                $sep = -1;
2156
                $j = $i;
2157
                $l = 0;
2158
                if ($nl == 1) {
2159
                    $this->x = $this->_left_margin;
2160
                    $width = $this->w - $this->_right_margin - $this->x;
2161
                    $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
2162
                }
2163
                $nl++;
2164
                continue;
2165
            }
2166
            if ($c == ' ') {
2167
                $sep = $i;
2168
                $ls = $l;
2169
            }
2170
            $l += (isset($cw[$c]) ? $cw[$c] : 0);
2171
            if ($l > $wmax) {
2172
                /* Automatic line break. */
2173
                if ($sep == -1) {
2174
                    if ($this->x > $this->_left_margin) {
2175
                        /* Move to next line. */
2176
                        $this->x = $this->_left_margin;
2177
                        $this->y += $height;
2178
                        $width = $this->w - $this->_right_margin - $this->x;
2179
                        $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
2180
                        $i++;
2181
                        $nl++;
2182
                        continue;
2183
                    }
2184
                    if ($i == $j) {
2185
                        $i++;
2186
                    }
2187
                    $result = $this->cell($width, $height,
2188
                                          substr($s, $j, $i - $j),
2189
                                          0, 2, '', 0, $link);
2190
                    if (is_a($result, 'PEAR_Error')) {
2191
                        return $result;
2192
                    }
2193
                } else {
2194
                    $result = $this->cell($width, $height,
2195
                                          substr($s, $j, $sep - $j),
2196
                                          0, 2, '', 0, $link);
2197
                    if (is_a($result, 'PEAR_Error')) {
2198
                        return $result;
2199
                    }
2200
                    $i = $sep + 1;
2201
                }
2202
                $sep = -1;
2203
                $j = $i;
2204
                $l = 0;
2205
                if ($nl == 1) {
2206
                    $this->x = $this->_left_margin;
2207
                    $width = $this->w - $this->_right_margin - $this->x;
2208
                    $wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
2209
                }
2210
                $nl++;
2211
            } else {
2212
                $i++;
2213
            }
2214
        }
2215
        /* Last chunk. */
2216
        if ($i != $j) {
2217
            $result = $this->cell($l / 1000 * $this->_font_size, $height,
2218
                                  substr($s, $j, $i), 0, 0, '', 0, $link);
2219
            if (is_a($result, 'PEAR_Error')) {
2220
                return $result;
2221
            }
2222
        }
2223
    }
2224
 
2225
    /**
2226
     * Writes text at an angle.
2227
     *
2228
     * All coordinates can be negative to provide values from the right or
2229
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
2230
     *
2231
     * @param integer $x         X coordinate.
2232
     * @param integer $y         Y coordinate.
2233
     * @param string $text       Text to write.
2234
     * @param float $text_angle  Angle to rotate (Eg. 90 = bottom to top).
2235
     * @param float $font_angle  Rotate characters as well as text.
2236
     *
2237
     * @see setFont()
2238
     */
2239
    function writeRotated($x, $y, $text, $text_angle, $font_angle = 0)
2240
    {
2241
        if ($x < 0) {
2242
            $x += $this->w;
2243
        }
2244
        if ($y < 0) {
2245
            $y += $this->h;
2246
        }
2247
 
2248
        /* Escape text. */
2249
        $text = $this->_escape($text);
2250
 
2251
        $font_angle += 90 + $text_angle;
2252
        $text_angle *= M_PI / 180;
2253
        $font_angle *= M_PI / 180;
2254
 
2255
        $text_dx = cos($text_angle);
2256
        $text_dy = sin($text_angle);
2257
        $font_dx = cos($font_angle);
2258
        $font_dy = sin($font_angle);
2259
 
2260
        $s= sprintf('BT %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' Tm (%s) Tj ET',
2261
                    $text_dx, $text_dy, $font_dx, $font_dy,
2262
                    $x * $this->_scale, ($this->h-$y) * $this->_scale, $text);
2263
 
2264
        if ($this->_draw_color) {
2265
            $s = 'q ' . $this->_draw_color . ' ' . $s . ' Q';
2266
        }
2267
        $this->_out($s);
2268
    }
2269
 
2270
    /**
2271
     * Prints an image in the page.
2272
     *
2273
     * The upper-left corner and at least one of the dimensions must be
2274
     * specified; the height or the width can be calculated automatically in
2275
     * order to keep the image proportions. Supported formats are JPEG and
2276
     * PNG.
2277
     *
2278
     * All coordinates can be negative to provide values from the right or
2279
     * bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
2280
     *
2281
     * For JPEG, all flavors are allowed:
2282
     *   - gray scales
2283
     *   - true colors (24 bits)
2284
     *   - CMYK (32 bits)
2285
     *
2286
     * For PNG, are allowed:
2287
     *   - gray scales on at most 8 bits (256 levels)
2288
     *   - indexed colors
2289
     *   - true colors (24 bits)
2290
     * but are not supported:
2291
     *   - Interlacing
2292
     *   - Alpha channel
2293
     *
2294
     * If a transparent color is defined, it will be taken into account (but
2295
     * will be only interpreted by Acrobat 4 and above).
2296
     * The format can be specified explicitly or inferred from the file
2297
     * extension.
2298
     * It is possible to put a link on the image.
2299
     *
2300
     * Remark: if an image is used several times, only one copy will be
2301
     * embedded in the file.
2302
     *
2303
     * @param string $file   Name of the file containing the image.
2304
     * @param float $x       Abscissa of the upper-left corner.
2305
     * @param float $y       Ordinate of the upper-left corner.
2306
     * @param float $width   Width of the image in the page. If equal to zero,
2307
     *                       it is automatically calculated to keep the
2308
     *                       original proportions.
2309
     * @param float $height  Height of the image in the page. If not specified
2310
     *                       or equal to zero, it is automatically calculated
2311
     *                       to keep the original proportions.
2312
     * @param string $type   Image format. Possible values are (case
2313
     *                       insensitive): JPG, JPEG, PNG. If not specified,
2314
     *                       the type is inferred from the file extension.
2315
     * @param mixed $link    URL or identifier returned by {@link addLink()}.
2316
     *
2317
     * @see addLink()
2318
     */
2319
    function image($file, $x, $y, $width = 0, $height = 0, $type = '',
2320
                   $link = '')
2321
    {
2322
        if ($x < 0) {
2323
            $x += $this->w;
2324
        }
2325
        if ($y < 0) {
2326
            $y += $this->h;
2327
        }
2328
 
2329
        if (!isset($this->_images[$file])) {
2330
            /* First use of image, get some file info. */
2331
            if ($type == '') {
2332
                $pos = strrpos($file, '.');
2333
                if ($pos === false) {
2334
                    return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file));
2335
                }
2336
                $type = substr($file, $pos + 1);
2337
            }
2338
            $type = strtolower($type);
2339
            $mqr = get_magic_quotes_runtime();
2340
            set_magic_quotes_runtime(0);
2341
            if ($type == 'jpg' || $type == 'jpeg') {
2342
                $info = $this->_parseJPG($file);
2343
            } elseif ($type == 'png') {
2344
                $info = $this->_parsePNG($file);
2345
            } else {
2346
                return $this->raiseError(sprintf('Unsupported image file type: %s', $type));
2347
            }
2348
            if (is_a($info, 'PEAR_Error')) {
2349
                return $info;
2350
            }
2351
            set_magic_quotes_runtime($mqr);
2352
            $info['i'] = count($this->_images) + 1;
2353
            $this->_images[$file] = $info;
2354
        } else {
2355
            $info = $this->_images[$file];
2356
        }
2357
 
2358
        /* Make sure all vars are converted to pt scale. */
2359
        $x      = $this->_toPt($x);
2360
        $y      = $this->hPt - $this->_toPt($y);
2361
        $width  = $this->_toPt($width);
2362
        $height = $this->_toPt($height);
2363
 
2364
        /* If not specified do automatic width and height calculations. */
2365
        if (empty($width) && empty($height)) {
2366
            $width = $info['w'];
2367
            $height = $info['h'];
2368
        } elseif (empty($width)) {
2369
            $width = $height * $info['w'] / $info['h'];
2370
        } elseif (empty($height)) {
2371
            $height = $width * $info['h'] / $info['w'];
2372
        }
2373
 
2374
        $this->_out(sprintf('q %.2' . FILE_PDF_FLOAT . ' 0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' cm /I%d Do Q', $width, $height, $x, $y - $height, $info['i']));
2375
 
2376
        /* Set any link if requested. */
2377
        if ($link) {
2378
            $this->_link($x, $y, $width, $height, $link);
2379
        }
2380
    }
2381
 
2382
    /**
2383
     * Performs a line break.
2384
     *
2385
     * The current abscissa goes back to the left margin and the ordinate
2386
     * increases by the amount passed in parameter.
2387
     *
2388
     * @param float $height  The height of the break. By default, the value
2389
     *                       equals the height of the last printed cell.
2390
     *
2391
     * @see cell()
2392
     */
2393
    function newLine($height = '')
2394
    {
2395
        $this->x = $this->_left_margin;
2396
        if (is_string($height)) {
2397
            $this->y += $this->_last_height;
2398
        } else {
2399
            $this->y += $height;
2400
        }
2401
    }
2402
 
2403
    /**
2404
     * Returns the abscissa of the current position in user units.
2405
     *
2406
     * @return float
2407
     *
2408
     * @see setX()
2409
     * @see getY()
2410
     * @see setY()
2411
     */
2412
    function getX()
2413
    {
2414
        return $this->x;
2415
    }
2416
 
2417
    /**
2418
     * Defines the abscissa of the current position.
2419
     *
2420
     * If the passed value is negative, it is relative to the right of the
2421
     * page.
2422
     *
2423
     * @param float $x  The value of the abscissa.
2424
     *
2425
     * @see getX()
2426
     * @see getY()
2427
     * @see setY()
2428
     * @see setXY()
2429
     */
2430
    function setX($x)
2431
    {
2432
        if ($x >= 0) {
2433
            /* Absolute value. */
2434
            $this->x = $x;
2435
        } else {
2436
            /* Negative, so relative to right edge of the page. */
2437
            $this->x = $this->w + $x;
2438
        }
2439
    }
2440
 
2441
    /**
2442
     * Returns the ordinate of the current position in user units.
2443
     *
2444
     * @return float
2445
     *
2446
     * @see setY()
2447
     * @see getX()
2448
     * @see setX()
2449
     */
2450
    function getY()
2451
    {
2452
        return $this->y;
2453
    }
2454
 
2455
    /**
2456
     * Defines the ordinate of the current position.
2457
     *
2458
     * If the passed value is negative, it is relative to the bottom of the
2459
     * page.
2460
     *
2461
     * @param float $y  The value of the ordinate.
2462
     *
2463
     * @see getX()
2464
     * @see getY()
2465
     * @see setY()
2466
     * @see setXY()
2467
     */
2468
    function setY($y)
2469
    {
2470
        if ($y >= 0) {
2471
            /* Absolute value. */
2472
            $this->y = $y;
2473
        } else {
2474
            /* Negative, so relative to bottom edge of the page. */
2475
            $this->y = $this->h + $y;
2476
        }
2477
    }
2478
 
2479
    /**
2480
     * Defines the abscissa and ordinate of the current position.
2481
     *
2482
     * If the passed values are negative, they are relative respectively to
2483
     * the right and bottom of the page.
2484
     *
2485
     * @param float $x  The value of the abscissa.
2486
     * @param float $y  The value of the ordinate.
2487
     *
2488
     * @see setX()
2489
     * @see setY()
2490
     */
2491
    function setXY($x, $y)
2492
    {
2493
        $this->setY($y);
2494
        $this->setX($x);
2495
    }
2496
 
2497
    /**
2498
     * Returns the current buffer content and resets the buffer.
2499
     *
2500
     * Use this method when creating large files to avoid memory problems.
2501
     * This method doesn't work in combination with the output() or save()
2502
     * methods, use getOutput() at the end. Calling this method doubles the
2503
     * memory usage during the call.
2504
     *
2505
     * @since File_PDF 0.2.0
2506
     * @since Horde 3.2
2507
     * @see getOutput()
2508
     */
2509
    function flush()
2510
    {
2511
        // Make sure we have the file header.
2512
        $this->_beginDoc();
2513
 
2514
        $buffer = $this->_buffer;
2515
        $this->_buffer = '';
2516
        $this->_flushed = true;
2517
        $this->_buflen += strlen($buffer);
2518
 
2519
        return $buffer;
2520
    }
2521
 
2522
    /**
2523
     * Returns the raw PDF file.
2524
     *
2525
     * @see output()
2526
     * @see flush()
2527
     */
2528
    function getOutput()
2529
    {
2530
        /* Check whether file has been closed. */
2531
        if ($this->_state < 3) {
2532
            $result = $this->close();
2533
            if (is_a($result, 'PEAR_Error')) {
2534
                return $result;
2535
            }
2536
        }
2537
 
2538
        return $this->_buffer;
2539
    }
2540
 
2541
    /**
2542
     * Sends the buffered data to the browser.
2543
     *
2544
     * @param string $filename  The filename for the output file.
2545
     * @param boolean $inline   True if inline, false if attachment.
2546
     */
2547
    function output($filename = 'unknown.pdf', $inline = false)
2548
    {
2549
        /* Check whether the buffer has been flushed already. */
2550
        if ($this->_flushed) {
2551
            return $this->raiseError('The buffer has been flushed already, don\'t use output() in combination with flush().');
2552
        }
2553
 
2554
        /* Check whether file has been closed. */
2555
        if ($this->_state < 3) {
2556
            $result = $this->close();
2557
            if (is_a($result, 'PEAR_Error')) {
2558
                return $result;
2559
            }
2560
        }
2561
 
2562
        /* Check if headers have been sent. */
2563
        if (headers_sent()) {
2564
            return $this->raiseError('Unable to send PDF file, some data has already been output to browser');
2565
        }
2566
 
2567
        /* If HTTP_Download is not available return a PEAR_Error. */
2568
        if (!include_once 'HTTP/Download.php') {
2569
            return $this->raiseError('Missing PEAR package HTTP_Download');
2570
        }
2571
 
2572
        /* Params for the output. */
2573
        $disposition = $inline ? HTTP_DOWNLOAD_INLINE : HTTP_DOWNLOAD_ATTACHMENT;
2574
        $params = array('data'               => $this->_buffer,
2575
                        'contenttype'        => 'application/pdf',
2576
                        'contentdisposition' => array($disposition, $filename));
2577
        /* Output the file. */
2578
        return HTTP_Download::staticSend($params);
2579
    }
2580
 
2581
    /**
2582
     * Saves the PDF file on the filesystem.
2583
     *
2584
     * @param string $filename  The filename for the output file.
2585
     */
2586
    function save($filename = 'unknown.pdf')
2587
    {
2588
        /* Check whether the buffer has been flushed already. */
2589
        if ($this->_flushed) {
2590
            return $this->raiseError('The buffer has been flushed already, don\'t use save() in combination with flush().');
2591
        }
2592
 
2593
        /* Check whether file has been closed. */
2594
        if ($this->_state < 3) {
2595
            $result = $this->close();
2596
            if (is_a($result, 'PEAR_Error')) {
2597
                return $result;
2598
            }
2599
        }
2600
 
2601
        $f = fopen($filename, 'wb');
2602
        if (!$f) {
2603
            return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename));
2604
        }
2605
        fwrite($f, $this->_buffer, strlen($this->_buffer));
2606
        fclose($f);
2607
    }
2608
 
2609
    function _toPt($val)
2610
    {
2611
        return $val * $this->_scale;
2612
    }
2613
 
2614
    function _getFontFile($fontkey, $path = '')
2615
    {
2616
        static $font_widths = array();
2617
 
2618
        if (!isset($font_widths[$fontkey])) {
2619
            if (!empty($path)) {
2620
                $file = $path . strtolower($fontkey) . '.php';
2621
            } else {
2622
                $file = dirname(__FILE__) . '/PDF/fonts/' . strtolower($fontkey) . '.php';
2623
            }
2624
            include $file;
2625
            if (!isset($font_widths[$fontkey])) {
2626
                return $this->raiseError(sprintf('Could not include font metric file: %s', $file));
2627
            }
2628
        }
2629
 
2630
        return $font_widths;
2631
    }
2632
 
2633
    function _link($x, $y, $width, $height, $link)
2634
    {
2635
        /* Save link to page links array. */
2636
        $this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link);
2637
    }
2638
 
2639
    function _beginDoc()
2640
    {
2641
        /* Start document, but only if not yet started. */
2642
        if ($this->_state < 1) {
2643
            $this->_state = 1;
2644
            $this->_out('%PDF-1.3');
2645
        }
2646
    }
2647
 
2648
    function _putPages()
2649
    {
2650
        $nb = $this->_page;
2651
        if (!empty($this->_alias_nb_pages)) {
2652
            /* Replace number of pages. */
2653
            for ($n = 1; $n <= $nb; $n++) {
2654
                $this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]);
2655
            }
2656
        }
2657
        if ($this->_default_orientation == 'P') {
2658
            $wPt = $this->fwPt;
2659
            $hPt = $this->fhPt;
2660
        } else {
2661
            $wPt = $this->fhPt;
2662
            $hPt = $this->fwPt;
2663
        }
2664
        $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
2665
        for ($n = 1; $n <= $nb; $n++) {
2666
            /* Page */
2667
            $this->_newobj();
2668
            $this->_out('<</Type /Page');
2669
            $this->_out('/Parent 1 0 R');
2670
            if (isset($this->_orientation_changes[$n])) {
2671
                $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $hPt, $wPt));
2672
            }
2673
            $this->_out('/Resources 2 0 R');
2674
            if (isset($this->_page_links[$n])) {
2675
                /* Links */
2676
                $annots = '/Annots [';
2677
                foreach ($this->_page_links[$n] as $pl) {
2678
                    $rect = sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . '', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
2679
                    $annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
2680
                    if (is_string($pl[4])) {
2681
                        $annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>';
2682
                    } else {
2683
                        $l = $this->_links[$pl[4]];
2684
                        $height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt;
2685
                        $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2' . FILE_PDF_FLOAT . ' null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale);
2686
                    }
2687
                }
2688
                $this->_out($annots . ']');
2689
            }
2690
            $this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
2691
            $this->_out('endobj');
2692
            /* Page content */
2693
            $p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
2694
            $this->_newobj();
2695
            $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
2696
            $this->_putStream($p);
2697
            $this->_out('endobj');
2698
        }
2699
        /* Pages root */
2700
        $this->_offsets[1] = $this->_buflen + strlen($this->_buffer);
2701
        $this->_out('1 0 obj');
2702
        $this->_out('<</Type /Pages');
2703
        $kids = '/Kids [';
2704
        for ($i = 0; $i < $nb; $i++) {
2705
            $kids .= (3 + 2 * $i) . ' 0 R ';
2706
        }
2707
        $this->_out($kids . ']');
2708
        $this->_out('/Count ' . $nb);
2709
        $this->_out(sprintf('/MediaBox [0 0 %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ']', $wPt, $hPt));
2710
        $this->_out('>>');
2711
        $this->_out('endobj');
2712
    }
2713
 
2714
    function _putFonts()
2715
    {
2716
        $nf = $this->_n;
2717
        foreach ($this->_diffs as $diff) {
2718
            /* Encodings */
2719
            $this->_newobj();
2720
            $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
2721
            $this->_out('endobj');
2722
        }
2723
        $mqr = get_magic_quotes_runtime();
2724
        set_magic_quotes_runtime(0);
2725
        foreach ($this->_font_files as $file => $info) {
2726
            /* Font file embedding. */
2727
            $this->_newobj();
2728
            $this->_font_files[$file]['n'] = $this->_n;
2729
            $size = filesize($file);
2730
            if (!$size) {
2731
                return $this->raiseError('Font file not found');
2732
            }
2733
            $this->_out('<</Length ' . $size);
2734
            if (substr($file, -2) == '.z') {
2735
                $this->_out('/Filter /FlateDecode');
2736
            }
2737
            $this->_out('/Length1 ' . $info['length1']);
2738
            if (isset($info['length2'])) {
2739
                $this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
2740
            }
2741
            $this->_out('>>');
2742
            $f = fopen($file, 'rb');
2743
            $this->_putStream(fread($f, $size));
2744
            fclose($f);
2745
            $this->_out('endobj');
2746
        }
2747
        set_magic_quotes_runtime($mqr);
2748
        foreach ($this->_fonts as $k => $font) {
2749
            /* Font objects */
2750
            $this->_newobj();
2751
            $this->_fonts[$k]['n'] = $this->_n;
2752
            $name = $font['name'];
2753
            $this->_out('<</Type /Font');
2754
            $this->_out('/BaseFont /' . $name);
2755
            if ($font['type'] == 'core') {
2756
                /* Standard font. */
2757
                $this->_out('/Subtype /Type1');
2758
                if ($name != 'Symbol' && $name != 'ZapfDingbats') {
2759
                    $this->_out('/Encoding /WinAnsiEncoding');
2760
                }
2761
            } else {
2762
                /* Additional font. */
2763
                $this->_out('/Subtype /' . $font['type']);
2764
                $this->_out('/FirstChar 32');
2765
                $this->_out('/LastChar 255');
2766
                $this->_out('/Widths ' . ($this->_n + 1) . ' 0 R');
2767
                $this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R');
2768
                if ($font['enc']) {
2769
                    if (isset($font['diff'])) {
2770
                        $this->_out('/Encoding ' . ($nf + $font['diff']) . ' 0 R');
2771
                    } else {
2772
                        $this->_out('/Encoding /WinAnsiEncoding');
2773
                    }
2774
                }
2775
            }
2776
            $this->_out('>>');
2777
            $this->_out('endobj');
2778
            if ($font['type'] != 'core') {
2779
                /* Widths. */
2780
                $this->_newobj();
2781
                $cw = &$font['cw'];
2782
                $s = '[';
2783
                for ($i = 32; $i <= 255; $i++) {
2784
                    $s .= $cw[chr($i)] . ' ';
2785
                }
2786
                $this->_out($s . ']');
2787
                $this->_out('endobj');
2788
                /* Descriptor. */
2789
                $this->_newobj();
2790
                $s = '<</Type /FontDescriptor /FontName /' . $name;
2791
                foreach ($font['desc'] as $k => $v) {
2792
                    $s .= ' /' . $k . ' ' . $v;
2793
                }
2794
                $file = $font['file'];
2795
                if ($file) {
2796
                    $s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R';
2797
                }
2798
                $this->_out($s . '>>');
2799
                $this->_out('endobj');
2800
            }
2801
        }
2802
    }
2803
 
2804
    function _putImages()
2805
    {
2806
        $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
2807
        foreach ($this->_images as $file => $info) {
2808
            $this->_newobj();
2809
            $this->_images[$file]['n'] = $this->_n;
2810
            $this->_out('<</Type /XObject');
2811
            $this->_out('/Subtype /Image');
2812
            $this->_out('/Width ' . $info['w']);
2813
            $this->_out('/Height ' . $info['h']);
2814
            if ($info['cs'] == 'Indexed') {
2815
                $this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1) . ' 0 R]');
2816
            } else {
2817
                $this->_out('/ColorSpace /' . $info['cs']);
2818
                if ($info['cs'] == 'DeviceCMYK') {
2819
                    $this->_out('/Decode [1 0 1 0 1 0 1 0]');
2820
                }
2821
            }
2822
            $this->_out('/BitsPerComponent ' . $info['bpc']);
2823
            $this->_out('/Filter /' . $info['f']);
2824
            if (isset($info['parms'])) {
2825
                $this->_out($info['parms']);
2826
            }
2827
            if (isset($info['trns']) && is_array($info['trns'])) {
2828
                $trns = '';
2829
                $i_max = count($info['trns']);
2830
                for ($i = 0; $i < $i_max; $i++) {
2831
                    $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
2832
                }
2833
                $this->_out('/Mask [' . $trns . ']');
2834
            }
2835
            $this->_out('/Length ' . strlen($info['data']) . '>>');
2836
            $this->_putStream($info['data']);
2837
            $this->_out('endobj');
2838
 
2839
            /* Palette. */
2840
            if ($info['cs'] == 'Indexed') {
2841
                $this->_newobj();
2842
                $pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal'];
2843
                $this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
2844
                $this->_putStream($pal);
2845
                $this->_out('endobj');
2846
            }
2847
        }
2848
    }
2849
 
2850
    function _putResources()
2851
    {
2852
        $this->_putFonts();
2853
        $this->_putImages();
2854
        /* Resource dictionary */
2855
        $this->_offsets[2] = $this->_buflen + strlen($this->_buffer);
2856
        $this->_out('2 0 obj');
2857
        $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
2858
        $this->_out('/Font <<');
2859
        foreach ($this->_fonts as $font) {
2860
            $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
2861
        }
2862
        $this->_out('>>');
2863
        if (count($this->_images)) {
2864
            $this->_out('/XObject <<');
2865
            foreach ($this->_images as $image) {
2866
                $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
2867
            }
2868
            $this->_out('>>');
2869
        }
2870
        $this->_out('>>');
2871
        $this->_out('endobj');
2872
    }
2873
 
2874
    function _putInfo()
2875
    {
2876
        $this->_out('/Producer ' . $this->_textString('Horde PDF'));
2877
        if (!empty($this->_info['title'])) {
2878
            $this->_out('/Title ' . $this->_textString($this->_info['title']));
2879
        }
2880
        if (!empty($this->_info['subject'])) {
2881
            $this->_out('/Subject ' . $this->_textString($this->_info['subject']));
2882
        }
2883
        if (!empty($this->_info['author'])) {
2884
            $this->_out('/Author ' . $this->_textString($this->_info['author']));
2885
        }
2886
        if (!empty($this->keywords)) {
2887
            $this->_out('/Keywords ' . $this->_textString($this->keywords));
2888
        }
2889
        if (!empty($this->creator)) {
2890
            $this->_out('/Creator ' . $this->_textString($this->creator));
2891
        }
2892
        $this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis')));
2893
    }
2894
 
2895
    function _putCatalog()
2896
    {
2897
        $this->_out('/Type /Catalog');
2898
        $this->_out('/Pages 1 0 R');
2899
        if ($this->_zoom_mode == 'fullpage') {
2900
            $this->_out('/OpenAction [3 0 R /Fit]');
2901
        } elseif ($this->_zoom_mode == 'fullwidth') {
2902
            $this->_out('/OpenAction [3 0 R /FitH null]');
2903
        } elseif ($this->_zoom_mode == 'real') {
2904
            $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
2905
        } elseif (!is_string($this->_zoom_mode)) {
2906
            $this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100) . ']');
2907
        }
2908
        if ($this->_layout_mode == 'single') {
2909
            $this->_out('/PageLayout /SinglePage');
2910
        } elseif ($this->_layout_mode == 'continuous') {
2911
            $this->_out('/PageLayout /OneColumn');
2912
        } elseif ($this->_layout_mode == 'two') {
2913
            $this->_out('/PageLayout /TwoColumnLeft');
2914
        }
2915
    }
2916
 
2917
    function _putTrailer()
2918
    {
2919
        $this->_out('/Size ' . ($this->_n + 1));
2920
        $this->_out('/Root ' . $this->_n . ' 0 R');
2921
        $this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
2922
    }
2923
 
2924
    function _endDoc()
2925
    {
2926
        $this->_putPages();
2927
        $this->_putResources();
2928
        /* Info */
2929
        $this->_newobj();
2930
        $this->_out('<<');
2931
        $this->_putInfo();
2932
        $this->_out('>>');
2933
        $this->_out('endobj');
2934
        /* Catalog */
2935
        $this->_newobj();
2936
        $this->_out('<<');
2937
        $this->_putCatalog();
2938
        $this->_out('>>');
2939
        $this->_out('endobj');
2940
        /* Cross-ref */
2941
        $o = $this->_buflen + strlen($this->_buffer);
2942
        $this->_out('xref');
2943
        $this->_out('0 ' . ($this->_n + 1));
2944
        $this->_out('0000000000 65535 f ');
2945
        for ($i = 1; $i <= $this->_n; $i++) {
2946
            $this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
2947
        }
2948
        /* Trailer */
2949
        $this->_out('trailer');
2950
        $this->_out('<<');
2951
        $this->_putTrailer();
2952
        $this->_out('>>');
2953
        $this->_out('startxref');
2954
        $this->_out($o);
2955
        $this->_out('%%EOF');
2956
        $this->_state = 3;
2957
    }
2958
 
2959
    function _beginPage($orientation)
2960
    {
2961
        $this->_page++;
2962
        $this->_pages[$this->_page] = '';
2963
        $this->_state = 2;
2964
        $this->x = $this->_left_margin;
2965
        $this->y = $this->_top_margin;
2966
        $this->_last_height = 0;
2967
        /* Page orientation */
2968
        if (!$orientation) {
2969
            $orientation = $this->_default_orientation;
2970
        } else {
2971
            $orientation = strtoupper($orientation[0]);
2972
            if ($orientation != $this->_default_orientation) {
2973
                $this->_orientation_changes[$this->_page] = true;
2974
            }
2975
        }
2976
        if ($orientation != $this->_current_orientation) {
2977
            /* Change orientation */
2978
            if ($orientation == 'P') {
2979
                $this->wPt = $this->fwPt;
2980
                $this->hPt = $this->fhPt;
2981
                $this->w   = $this->fw;
2982
                $this->h   = $this->fh;
2983
            } else {
2984
                $this->wPt = $this->fhPt;
2985
                $this->hPt = $this->fwPt;
2986
                $this->w   = $this->fh;
2987
                $this->h   = $this->fw;
2988
            }
2989
            $this->_page_break_trigger = $this->h - $this->_break_margin;
2990
            $this->_current_orientation = $orientation;
2991
        }
2992
    }
2993
 
2994
    function _endPage()
2995
    {
2996
        /* End of page contents */
2997
        $this->_state = 1;
2998
    }
2999
 
3000
    function _newobj()
3001
    {
3002
        /* Begin a new object */
3003
        $this->_n++;
3004
        $this->_offsets[$this->_n] = $this->_buflen + strlen($this->_buffer);
3005
        $this->_out($this->_n . ' 0 obj');
3006
    }
3007
 
3008
    function _doUnderline($x, $y, $text)
3009
    {
3010
        /* Set the rectangle width according to text width. */
3011
        $width  = $this->getStringWidth($text, true);
3012
 
3013
        /* Set rectangle position and height, using underline position and
3014
         * thickness settings scaled by the font size. */
3015
        $y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000);
3016
        $height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000;
3017
 
3018
        return sprintf('%.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' %.2' . FILE_PDF_FLOAT . ' re f', $x, $y, $width, $height);
3019
    }
3020
 
3021
    function _parseJPG($file)
3022
    {
3023
        /* Extract info from a JPEG file. */
3024
        $img = @getimagesize($file);
3025
        if (!$img) {
3026
            return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file));
3027
        }
3028
        if ($img[2] != 2) {
3029
            return $this->raiseError(sprintf('Not a JPEG file: %s', $file));
3030
        }
3031
        if (!isset($img['channels']) || $img['channels'] == 3) {
3032
            $colspace = 'DeviceRGB';
3033
        } elseif ($img['channels'] == 4) {
3034
            $colspace = 'DeviceCMYK';
3035
        } else {
3036
            $colspace = 'DeviceGray';
3037
        }
3038
        $bpc = isset($img['bits']) ? $img['bits'] : 8;
3039
 
3040
        /* Read whole file. */
3041
        $f = fopen($file, 'rb');
3042
        $data = fread($f, filesize($file));
3043
        fclose($f);
3044
 
3045
        return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3046
    }
3047
 
3048
    function _parsePNG($file)
3049
    {
3050
        /* Extract info from a PNG file. */
3051
        $f = fopen($file, 'rb');
3052
        if (!$f) {
3053
            return $this->raiseError(sprintf('Unable to open image file: %s', $file));
3054
        }
3055
 
3056
        /* Check signature. */
3057
        if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
3058
            return $this->raiseError(sprintf('Not a PNG file: %s', $file));
3059
        }
3060
 
3061
        /* Read header chunk. */
3062
        fread($f, 4);
3063
        if (fread($f, 4) != 'IHDR') {
3064
            return $this->raiseError(sprintf('Incorrect PNG file: %s', $file));
3065
        }
3066
        $width = $this->_freadInt($f);
3067
        $height = $this->_freadInt($f);
3068
        $bpc = ord(fread($f, 1));
3069
        if ($bpc > 8) {
3070
            return $this->raiseError(sprintf('16-bit depth not supported: %s', $file));
3071
        }
3072
        $ct = ord(fread($f, 1));
3073
        if ($ct == 0) {
3074
            $colspace = 'DeviceGray';
3075
        } elseif ($ct == 2) {
3076
            $colspace = 'DeviceRGB';
3077
        } elseif ($ct == 3) {
3078
            $colspace = 'Indexed';
3079
        } else {
3080
            return $this->raiseError(sprintf('Alpha channel not supported: %s', $file));
3081
        }
3082
        if (ord(fread($f, 1)) != 0) {
3083
            return $this->raiseError(sprintf('Unknown compression method: %s', $file));
3084
        }
3085
        if (ord(fread($f, 1)) != 0) {
3086
            return $this->raiseError(sprintf('Unknown filter method: %s', $file));
3087
        }
3088
        if (ord(fread($f, 1)) != 0) {
3089
            return $this->raiseError(sprintf('Interlacing not supported: %s', $file));
3090
        }
3091
        fread($f, 4);
3092
        $parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $width . '>>';
3093
        /* Scan chunks looking for palette, transparency and image data. */
3094
        $pal = '';
3095
        $trns = '';
3096
        $data = '';
3097
        do {
3098
            $n = $this->_freadInt($f);
3099
            $type = fread($f, 4);
3100
            if ($type == 'PLTE') {
3101
                /* Read palette */
3102
                $pal = fread($f, $n);
3103
                fread($f, 4);
3104
            } elseif ($type == 'tRNS') {
3105
                /* Read transparency info */
3106
                $t = fread($f, $n);
3107
                if ($ct == 0) {
3108
                    $trns = array(ord(substr($t, 1, 1)));
3109
                } elseif ($ct == 2) {
3110
                    $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
3111
                } else {
3112
                    $pos = strpos($t, chr(0));
3113
                    if (is_int($pos)) {
3114
                        $trns = array($pos);
3115
                    }
3116
                }
3117
                fread($f, 4);
3118
            } elseif ($type == 'IDAT') {
3119
                /* Read image data block */
3120
                $data .= fread($f, $n);
3121
                fread($f, 4);
3122
            } elseif ($type == 'IEND') {
3123
                break;
3124
            } else {
3125
                fread($f, $n + 4);
3126
            }
3127
        } while ($n);
3128
 
3129
        if ($colspace == 'Indexed' && empty($pal)) {
3130
            return $this->raiseError(sprintf('Missing palette in: %s', $file));
3131
        }
3132
        fclose($f);
3133
 
3134
        return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3135
    }
3136
 
3137
    function _freadInt($f)
3138
    {
3139
        /* Read a 4-byte integer from file. */
3140
        $i  = ord(fread($f, 1)) << 24;
3141
        $i += ord(fread($f, 1)) << 16;
3142
        $i += ord(fread($f, 1)) << 8;
3143
        $i += ord(fread($f, 1));
3144
        return $i;
3145
    }
3146
 
3147
    function _textString($s)
3148
    {
3149
        /* Format a text string */
3150
        return '(' . $this->_escape($s) . ')';
3151
    }
3152
 
3153
    function _escape($s)
3154
    {
3155
        /* Add \ before \, ( and ) */
3156
        return str_replace(array('\\', ')', '('),
3157
                           array('\\\\', '\\)', '\\('),
3158
                           $s);
3159
    }
3160
 
3161
    function _putStream($s)
3162
    {
3163
        $this->_out('stream');
3164
        $this->_out($s);
3165
        $this->_out('endstream');
3166
    }
3167
 
3168
    function _out($s)
3169
    {
3170
        /* Add a line to the document. */
3171
        if ($this->_state == 2) {
3172
            $this->_pages[$this->_page] .= $s . "\n";
3173
        } else {
3174
            $this->_buffer .= $s . "\n";
3175
        }
3176
    }
3177
 
3178
}