Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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