1<?php
2/*******************************************************************************
3 * Software: FPDF                                                               *
4 * Version:  1.53                                                               *
5 * Date:     2004-12-31                                                         *
6 * Author:   Olivier PLATHEY                                                    *
7 * License:  Freeware                                                           *
8 *                                                                              *
9 * You may use, modify and redistribute this software as you wish.              *
10 *******************************************************************************/
11
12/**
13 * Heavily patched to adapt to the HTML2PS/HTML2PDF script requirements by
14 * Konstantin Bournayev (bkon@bkon.ru)
15 */
16
17if (!class_exists('FPDF')) {
18  define('FPDF_VERSION','1.53');
19
20  /**
21   * See PDF Reference 1.6 p.664 for explanation of flags specific to submit form action
22   */
23  define('PDF_SUBMIT_FORM_HTML',        1 << 2); // 1 - HTML, 0 - FDF
24  define('PDF_SUBMIT_FORM_COORDINATES', 1 << 4);
25
26  /**
27   * See PDF Reference 1.6 p.656 for explanation of flags specific to choice fields
28   */
29  define('PDF_FIELD_CHOICE_COMBO', 1 << 17);
30
31  /**
32   * See PDF Reference 1.6 p.653 for explanation of flags specific to text fields
33   */
34  define('PDF_FIELD_TEXT_MULTILINE',1 << 12);
35  define('PDF_FIELD_TEXT_PASSWORD', 1 << 13);
36  define('PDF_FIELD_TEXT_FILE',     1 << 20);
37
38  /**
39   * See PDF Reference 1.6 p.663 for examplanation of flags specific to for submit actions
40   */
41  define("PDF_FORM_SUBMIT_EXCLUDE", 1 << 0);
42  define("PDF_FORM_SUBMIT_NOVALUE", 1 << 1);
43  define("PDF_FORM_SUBMIT_EFORMAT", 1 << 2);
44  define("PDF_FORM_SUBMIT_GET",     1 << 3);
45
46  class PDFIndirectObject {
47    var $object_id;
48    var $generation_id;
49
50    function get_object_id() {
51      return $this->object_id;
52    }
53
54    function get_generation_id() {
55      return $this->generation_id;
56    }
57
58    /**
59     * Outputs the PDF indirect object to PDF file.
60     *
61     * To pervent infinite loop on circular references, this method checks
62     * if current object have been already written to the file.
63     *
64     * Note that, in general, nested objects should be written to PDF file
65     * here too; this task is accomplished by calling _out_nested method,
66     * which should be overridden by children classes.
67     *
68     * @param FPDF $handler PDF file wrapper (FPDF object)
69     *
70     * @final
71     *
72     * @see FPDF::is_object_written
73     * @see PDFIndirectObject::_out_nested
74     */
75    function out(&$handler) {
76      if (!$handler->is_object_written($this->get_object_id())) {
77        $handler->offsets[$this->get_object_id()] = strlen($handler->buffer);
78        $handler->_out($handler->_indirect_object($this));
79
80        $this->_out_nested($handler);
81      };
82    }
83
84    /**
85     * Writes all nested objects to the PDF file. Should be overridden by
86     * PDFIndirectObject descendants.
87     *
88     * @param FPDF $handler PDF file wrapper (FPDF object)
89     *
90     * @see PDFIndirectObject::out
91     */
92    function _out_nested(&$handler) {
93      return true;
94    }
95
96    function PDFIndirectObject(&$handler,
97                               $object_id,
98                               $generation_id) {
99      $this->object_id = $object_id;
100      $this->generation_id = $generation_id;
101    }
102
103    function pdf(&$handler) {
104      return $handler->_dictionary($this->_dict($handler));
105    }
106
107    function _dict() {
108      return array();
109    }
110  }
111
112  class PDFPage extends PDFIndirectObject {
113    var $annotations;
114
115    function PDFPage(&$handler,
116                     $object_id,
117                     $generation_id) {
118      $this->PDFIndirectObject($handler,
119                               $object_id,
120                               $generation_id);
121    }
122
123    function add_annotation(&$annotation) {
124      $this->annotations[] =& $annotation;
125    }
126
127    function _annotations(&$handler) {
128      return $handler->_reference_array($this->annotations);
129    }
130  }
131
132  class PDFAppearanceStream extends PDFIndirectObject {
133    var $_content;
134
135    function PDFAppearanceStream(&$handler,
136                                 $object_id,
137                                 $generation_id,
138                                 $content) {
139      $this->PDFIndirectObject($handler,
140                               $object_id,
141                               $generation_id);
142
143      $this->_content = $content;
144    }
145
146    function pdf(&$handler) {
147      $dict_content   = array(
148                              'Type'     => "/XObject",
149                              'Subtype'  => "/Form",
150                              'FormType' => "1",
151                              'BBox'     => "[0 0 100 100]",
152                              'Matrix'   => "[1 0 0 1 0 0]",
153                              'Resources'=> "2 0 R",
154                              'Length'   => strlen($this->_content)
155                              );
156
157      $content = $handler->_dictionary($dict_content);
158      $content .= "\n";
159      $content .= $handler->_stream($this->_content);
160
161      return $content;
162    }
163  }
164
165  class PDFAnnotation extends PDFIndirectObject {
166    function PDFAnnotation(&$handler,
167                           $object_id,
168                           $generation_id) {
169      $this->PDFIndirectObject($handler,
170                               $object_id,
171                               $generation_id);
172    }
173
174    function _dict(&$handler) {
175      return array_merge(parent::_dict($handler),
176                         array("Type" => $handler->_name("Annot")));
177    }
178  }
179
180  class PDFRect {
181    var $x;
182    var $y;
183    var $w;
184    var $h;
185
186    function PDFRect($x,$y,$w,$h) {
187      $this->x = $x;
188      $this->y = $y;
189      $this->w = $w;
190      $this->h = $h;
191    }
192
193    function left(&$handler) {
194      return $handler->x_coord($this->x);
195    }
196
197    function right(&$handler) {
198      return $handler->x_coord($this->x+$this->w);
199    }
200
201    function top(&$handler) {
202      return $handler->y_coord($this->y);
203    }
204
205    function bottom(&$handler) {
206      return $handler->y_coord($this->y+$this->h);
207    }
208
209    function pdf(&$handler) {
210      return $handler->_array(sprintf("%.2f %.2f %.2f %.2f",
211                                      $this->left($handler),
212                                      $this->top($handler),
213                                      $this->right($handler),
214                                      $this->bottom($handler)));
215    }
216  }
217
218  class PDFAnnotationExternalLink extends PDFAnnotation {
219    var $rect;
220    var $link;
221
222    function PDFAnnotationExternalLink(&$handler,
223                                       $object_id,
224                                       $generation_id,
225                                       $rect,
226                                       $link) {
227      $this->PDFAnnotation($handler,
228                           $object_id,
229                           $generation_id);
230
231      $this->rect = $rect;
232      $this->link = $link;
233    }
234
235    function _dict(&$handler) {
236      return array_merge(parent::_dict($handler),
237                         array(
238                               'Subtype' => "/Link",
239                               'Rect'    => $this->rect->pdf($handler),
240                               'Border'  => "[0 0 0]",
241                               'A'       => "<</S /URI /URI ".$handler->_textstring($this->link).">>"
242                               ));
243    }
244  }
245
246  class PDFAnnotationInternalLink extends PDFAnnotation {
247    var $rect;
248    var $link;
249
250    function PDFAnnotationInternalLink(&$handler,
251                                       $object_id,
252                                       $generation_id,
253                                       $rect,
254                                       $link) {
255      $this->PDFAnnotation($handler,
256                           $object_id,
257                           $generation_id);
258
259      $this->rect = $rect;
260      $this->link = $link;
261    }
262
263    function pdf(&$handler) {
264      if ($handler->DefOrientation=='P') {
265        $wPt=$handler->fwPt;
266        $hPt=$handler->fhPt;
267      } else {
268        $wPt=$handler->fhPt;
269        $hPt=$handler->fwPt;
270      };
271      $l = $handler->links[$this->link];
272      $h = isset($handler->OrientationChanges[$l[0]]) ? $wPt : $hPt;
273
274      /**
275       * Sometimes hyperlinks may refer to pages NOT present in PDF document
276       * Example: a very long frame content; it it trimmed to one page, as
277       * framesets newer take more than one frame. A link targe which should be rendered
278       * on third page without frames will be never rendered at all.
279       *
280       * In this case we should disable link at all to prevent error from appearing
281       */
282
283      if (!isset($handler->_pages[$l[0]-1])) {
284        return "";
285      }
286
287      $content = $handler->_dictionary(array(
288                                             'Type'    => "/Annot",
289                                             'Subtype' => "/Link",
290                                             'Rect'    => $this->rect->pdf($handler),
291                                             'Border'  => "[0 0 0]",
292                                             'Dest'    => sprintf("[%s /XYZ 0 %.2f null]",
293                                                                  $handler->_reference($handler->_pages[$l[0]-1]),
294                                                                  $h-$l[1]*$handler->k)
295                                             ));
296      return $content;
297    }
298  }
299
300  class PDFAnnotationWidget extends PDFAnnotation {
301    var $_rect;
302
303    function PDFAnnotationWidget(&$handler,
304                                 $object_id,
305                                 $generation_id,
306                                 $rect) {
307      $this->PDFAnnotation($handler,
308                           $object_id,
309                           $generation_id);
310
311      $this->_rect = $rect;
312    }
313
314    function _dict(&$handler) {
315      return array_merge(parent::_dict($handler),
316                         array("Subtype" => $handler->_name("Widget"),
317                               'Rect'    => $this->_rect->pdf($handler)));
318    }
319  }
320
321  /**
322   * Generic PDF Form
323   */
324  class PDFFieldGroup extends PDFIndirectObject {
325    var $_kids;
326    var $_group_name;
327
328    function PDFFieldGroup(&$handler,
329                           $object_id,
330                           $generation_id,
331                           $group_name) {
332      $this->PDFIndirectObject($handler,
333                               $object_id,
334                               $generation_id);
335
336      /**
337       * Generate default group name, if needed
338       */
339      if (is_null($group_name) || $group_name == "") {
340        $group_name = sprintf("FieldGroup%d", $this->get_object_id());
341      };
342      $this->_group_name = $group_name;
343
344      $this->_kids = array();
345    }
346
347    function _check_field_name($field) {
348      /**
349       * Check if field name is empty
350       */
351      if (trim($field->get_field_name()) == "") {
352        error_log(sprintf("Found form field with empty name"));
353        return false;
354      };
355
356      /**
357       * Check if field name is unique inside this form! If we will not do it,
358       * some widgets may become inactive (ignored by PDF Reader)
359       */
360      foreach ($this->_kids as $kid) {
361        if ($kid->get_field_name() == $field->get_field_name()) {
362          error_log(sprintf("Interactive form '%s' already contains field named '%s'",
363                            $this->_group_name,
364                            $kid->get_field_name()));
365          return false;
366        }
367      };
368
369      return true;
370    }
371
372    function add_field(&$field) {
373      if (!$this->_check_field_name($field)) {
374        /**
375         * Field name is not unique; replace it with automatically-generated one
376         */
377        $field->set_field_name(sprintf("%s_FieldObject%d",
378                                       $field->get_field_name(),
379                                       $field->get_object_id()));
380      };
381
382      $this->_kids[] =& $field;
383      $field->set_parent($this);
384    }
385
386    function _dict(&$handler) {
387      return array_merge(parent::_dict($handler),
388                         array("Kids" => $handler->_reference_array($this->_kids),
389                               "T"    => $handler->_textstring($this->_group_name)));
390      return $content;
391    }
392
393    function _out_nested(&$handler) {
394      parent::_out_nested($handler);
395
396      foreach ($this->_kids as $field) {
397        $field->out($handler);
398      }
399    }
400  }
401
402  /**
403   * Generic superclass for all PDF interactive field widgets
404   */
405  class PDFField extends PDFAnnotationWidget {
406    /**
407     * @var string Partial field name (see PDF Specification 1.6 p.638 for explanation on "partial" and
408     * "fully qualified" field names
409     * @access private
410     */
411    var $_field_name;
412
413    /**
414     * @var PDFFieldGroup REference to a containing form object
415     * @access private
416     */
417    var $_parent;
418
419    function PDFField(&$handler,
420                      $object_id,
421                      $generation_id,
422                      $rect,
423                      $field_name) {
424      $this->PDFAnnotationWidget($handler,
425                                 $object_id,
426                                 $generation_id,
427                                 $rect);
428
429      /**
430       * Generate default field name, if needed
431       * @TODO: validate field_name contents
432       */
433      if (is_null($field_name) || $field_name == "") {
434        $field_name = sprintf("FieldObject%d", $this->get_object_id());
435      };
436
437      $this->_field_name = $field_name;
438    }
439
440    function get_field_name() {
441      if ($this->_field_name) {
442        return $this->_field_name;
443      } else {
444        return sprintf("FormObject%d", $this->get_object_id());
445      };
446    }
447
448    function _dict(&$handler) {
449      return array_merge(parent::_dict($handler),
450                         array("Parent" => $handler->_reference($this->_parent),
451                               "T"      => $handler->_textstring($this->get_field_name())));
452    }
453
454    function pdf(&$handler) {
455      return $handler->_dictionary($this->_dict($handler));
456    }
457
458    function set_field_name($value) {
459      $this->_field_name = $value;
460    }
461
462    function set_parent(&$form) {
463      $this->_parent =& $form;
464    }
465
466    function get_parent() {
467      return $this->_parent;
468    }
469  }
470
471  /**
472   * Checkbox interactive form widget
473   */
474  class PDFFieldCheckBox extends PDFField {
475    var $_value;
476    var $_appearance_on;
477    var $_appearance_off;
478    var $_checked;
479
480    function PDFFieldCheckBox(&$handler,
481                              $object_id,
482                              $generation_id,
483                              $rect,
484                              $field_name,
485                              $value,
486                              $checked) {
487      $this->PDFField($handler,
488                      $object_id,
489                      $generation_id,
490                      $rect,
491                      $field_name);
492
493      $this->_value = $value;
494      $this->_checked = $checked;
495
496      $this->_appearance_on = new PDFAppearanceStream($handler,
497                                                      $handler->_generate_new_object_number(),
498                                                      $generation_id,
499                                                      "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
500
501      $this->_appearance_off = new PDFAppearanceStream($handler,
502                                                       $handler->_generate_new_object_number(),
503                                                       $generation_id,
504                                                       "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
505    }
506
507    function _dict(&$handler) {
508      return array_merge(parent::_dict($handler),
509                         array(
510                               'FT'      => '/Btn',
511                               'Ff'      => sprintf("%d", 0),
512                               'TU'      => "<FEFF>",
513                               'MK'      => "<< /CA (3) >>",
514                               'DV'      => $this->_checked ? $handler->_name($this->_value) : "/Off",
515                               'V'       => $this->_checked ? $handler->_name($this->_value) : "/Off",
516                               'AP'      => sprintf("<< /N << /%s %s /Off %s >> >>",
517                                                    $this->_value,
518                                                    $handler->_reference($this->_appearance_on),
519                                                    $handler->_reference($this->_appearance_off))
520                               )
521                         );
522    }
523
524    function _out_nested(&$handler) {
525      parent::_out_nested($handler);
526
527      $this->_appearance_on->out($handler);
528      $this->_appearance_off->out($handler);
529    }
530  }
531
532  class PDFFieldPushButton extends PDFField {
533    var $_appearance;
534    var $fontindex;
535    var $fontsize;
536
537    function _out_nested(&$handler) {
538      parent::_out_nested($handler);
539
540      $this->_appearance->out($handler);
541    }
542
543    function PDFFieldPushButton(&$handler,
544                                $object_id,
545                                $generation_id,
546                                $rect,
547                                $fontindex,
548                                $fontsize) {
549      $this->PDFField($handler,
550                      $object_id,
551                      $generation_id,
552                      $rect,
553                      null);
554      $this->fontindex = $fontindex;
555      $this->fontsize  = $fontsize;
556
557      $this->_appearance = new PDFAppearanceStream($handler,
558                                                   $handler->_generate_new_object_number(),
559                                                   $generation_id,
560                                                   "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
561    }
562
563    function _action(&$handler) {
564      return "<< >>";
565    }
566
567    function _dict(&$handler) {
568      return array_merge(parent::_dict($handler),
569                         array(
570                               'FT'      => '/Btn',
571                               'Ff'      => sprintf("%d", 1 << 16),
572                               'TU'      => "<FEFF>",
573                               'DR'      => "2 0 R",
574                               'DA'      => sprintf("(0 0 0 rg /F%d %.2f Tf)",
575                                                    $this->fontindex,
576                                                    $this->fontsize),
577                               'AP'      => "<< /N ".$handler->_reference($this->_appearance)." >>",
578                               'AA'      => $this->_action($handler)
579                               ));
580    }
581  }
582
583  class PDFFieldPushButtonImage extends PDFFieldPushButton {
584    var $_link;
585
586    function PDFFieldPushButtonImage(&$handler,
587                                      $object_id,
588                                      $generation_id,
589                                      $rect,
590                                      $fontindex,
591                                      $fontsize,
592                                      $field_name,
593                                      $value,
594                                      $link) {
595      $this->PDFFieldPushButton($handler,
596                                $object_id,
597                                $generation_id,
598                                $rect,
599                                $fontindex,
600                                $fontsize);
601
602      $this->_link  = $link;
603      $this->set_field_name($field_name);
604    }
605
606    function _action(&$handler) {
607      $action = $handler->_dictionary(array(
608                                            'S'     => "/SubmitForm",
609                                            'F'     => $handler->_textstring($this->_link),
610                                            'Fields'=> $handler->_reference_array(array($this->get_parent())),
611                                            'Flags' => PDF_SUBMIT_FORM_HTML | PDF_SUBMIT_FORM_COORDINATES
612                                            )
613                                      );
614      return $handler->_dictionary(array('U' => $action));
615    }
616  }
617
618  class PDFFieldPushButtonSubmit extends PDFFieldPushButton {
619    var $_link;
620    var $_caption;
621
622    function PDFFieldPushButtonSubmit(&$handler,
623                                      $object_id,
624                                      $generation_id,
625                                      $rect,
626                                      $fontindex,
627                                      $fontsize,
628                                      $field_name,
629                                      $value,
630                                      $link) {
631      $this->PDFFieldPushButton($handler,
632                                $object_id,
633                                $generation_id,
634                                $rect,
635                                $fontindex,
636                                $fontsize);
637
638      $this->_link    = $link;
639      $this->_caption = $value;
640      $this->set_field_name($field_name);
641    }
642
643    function _action(&$handler) {
644      $action = $handler->_dictionary(array(
645                                            'S'     => "/SubmitForm",
646                                            'F'     => $handler->_textstring($this->_link),
647                                            'Fields'=> $handler->_reference_array(array($this->get_parent())),
648                                            'Flags' =>
649                                            (1 << 2) // ExportFormat (HTML)
650                                            )
651                                      );
652      return $handler->_dictionary(array('U' => $action));
653    }
654  }
655
656  class PDFFieldPushButtonReset extends PDFFieldPushButton {
657    function PDFFieldPushButtonReset(&$handler,
658                                     $object_id,
659                                     $generation_id,
660                                     $rect,
661                                     $fontindex,
662                                     $fontsize) {
663      $this->PDFFieldPushButton($handler,
664                                $object_id,
665                                $generation_id,
666                                $rect,
667                                $fontindex,
668                                $fontsize);
669    }
670
671    function _action(&$handler) {
672      $action = $handler->_dictionary(array('S' => "/ResetForm"));
673      return $handler->_dictionary(array('U' => $action));
674    }
675  }
676
677  /**
678   * Radio button inside the group.
679   *
680   * Note that radio button is not a field itself; only a group of radio buttons
681   * should have name.
682   */
683  class PDFFieldRadio extends PDFAnnotationWidget {
684    /**
685     * @var PDFFieldRadioGroup reference to a radio button group
686     * @access private
687     */
688    var $_parent;
689
690    /**
691     * @var String value of this radio button
692     * @access private
693     */
694    var $_value;
695
696    var $_appearance_on;
697    var $_appearance_off;
698
699    function PDFFieldRadio(&$handler,
700                           $object_id,
701                           $generation_id,
702                           $rect,
703                           $value) {
704      $this->PDFAnnotationWidget($handler,
705                                 $object_id,
706                                 $generation_id,
707                                 $rect);
708
709      $this->_value = $value;
710
711      $this->_appearance_on = new PDFAppearanceStream($handler,
712                                                      $handler->_generate_new_object_number(),
713                                                      $generation_id,
714                                                      "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
715
716      $this->_appearance_off = new PDFAppearanceStream($handler,
717                                                       $handler->_generate_new_object_number(),
718                                                       $generation_id,
719                                                       "Q 0 0 1 rg BT /F1 10 Tf 0 0 Td (8) Tj ET q");
720    }
721
722    function _dict(&$handler) {
723      return array_merge(parent::_dict($handler),
724                         array(
725                               'MK'      => "<< /CA (l) >>",
726                               'Parent'  => $handler->_reference($this->_parent),
727                               'AP'      => sprintf("<< /N << /%s %s /Off %s >> >>",
728                                                    $this->_value,
729                                                    $handler->_reference($this->_appearance_on),
730                                                    $handler->_reference($this->_appearance_off))
731                               ));
732    }
733
734    function _out_nested(&$handler) {
735      parent::_out_nested($handler);
736
737      $this->_appearance_on->out($handler);
738      $this->_appearance_off->out($handler);
739    }
740
741    /**
742     * Set a reference to the radio button group containing this group
743     *
744     * @param PDFFieldRadioGroup $parent reference to a group object
745     */
746    function set_parent(&$parent) {
747      $this->_parent =& $parent;
748    }
749  }
750
751  /**
752   * Create new group of radio buttons
753   */
754  class PDFFieldRadioGroup extends PDFFieldGroup {
755    var $_parent;
756    var $_checked;
757
758    function _dict($handler) {
759      return array_merge(parent::_dict($handler),
760                         array(
761                               'DV'      => $this->_checked ? $handler->_name($this->_checked) : "/Off",
762                               'V'       => $this->_checked ? $handler->_name($this->_checked) : "/Off",
763                               "FT"      => $handler->_name('Btn'),
764                               "Ff"      => sprintf("%d", 1 << 15),
765                               "Parent"  => $handler->_reference($this->_parent)
766                               ));
767    }
768
769    function _check_field_name($field) {
770      /**
771       * As radio buttons always have same field name, no checking should be made here
772       */
773
774      return true;
775    }
776
777    function PDFFieldRadioGroup(&$handler,
778                                $object_id,
779                                $generation_id,
780                                $group_name) {
781      $this->PDFFieldGroup($handler,
782                           $object_id,
783                           $generation_id,
784                           $group_name);
785
786      $this->_checked = null;
787    }
788
789    /**
790     * @return String name of the radio group
791     */
792    function get_field_name() {
793      return $this->_group_name;
794    }
795
796    function set_checked($value) {
797      $this->_checked = $value;
798    }
799
800    function set_parent(&$parent) {
801      $this->_parent =& $parent;
802    }
803  }
804
805  class PDFFieldSelect extends PDFField {
806    var $_options;
807    var $_value;
808
809    function _dict(&$handler) {
810      $options = array();
811      foreach ($this->_options as $arr) {
812        $options[] = $handler->_array(sprintf("%s %s",
813                                              $handler->_textstring($arr[0]),
814                                              $handler->_textstring($arr[1])));
815      };
816
817      $options_str = $handler->_array(implode(" ",$options));
818
819      return array_merge(parent::_dict($handler),
820                         array('FT'      => '/Ch',
821                               'Ff'      => PDF_FIELD_CHOICE_COMBO,
822                               'V'       => $handler->_textstring($this->_value), // Current value
823                               'DV'      => $handler->_textstring($this->_value), // Default value
824                               'DR'      => "2 0 R",
825                               'Opt'     => $options_str));
826    }
827
828    function PDFFieldSelect(&$handler,
829                            $object_id,
830                            $generation_id,
831                            $rect,
832                            $field_name,
833                            $value,
834                            $options) {
835      $this->PDFField($handler,
836                      $object_id,
837                      $generation_id,
838                      $rect,
839                      $field_name);
840
841      $this->_options = $options;
842      $this->_value   = $value;
843    }
844  }
845
846  /**
847   * Interactive text input
848   */
849  class PDFFieldText extends PDFField {
850    var $fontindex;
851    var $fontsize;
852
853    var $_appearance;
854
855    /**
856     * @var String contains the default value of this text field
857     * @access private
858     */
859    var $_value;
860
861    function _dict(&$handler) {
862      return array_merge(parent::_dict($handler),
863                         array(
864                               'FT'      => '/Tx',
865                               'V'       => $handler->_textstring($this->_value), // Current value
866                               'DV'      => $handler->_textstring($this->_value), // Default value
867                               'DR'      => "2 0 R",
868                               // @TODO fix font references
869                               'DA'      => sprintf("(0 0 0 rg /FF%d %.2f Tf)",
870                                                    $this->fontindex,
871                                                    $this->fontsize),
872//                                'AP'      => $handler->_dictionary(array("N" => $handler->_reference($this->_appearance))),
873                               ));
874    }
875
876    function _out_nested(&$handler) {
877      //      $this->_appearance->out($handler);
878    }
879
880    function PDFFieldText(&$handler,
881                          $object_id,
882                          $generation_id,
883                          $rect,
884                          $field_name,
885                          $value,
886                          $fontindex,
887                          $fontsize) {
888      $this->PDFField($handler,
889                      $object_id,
890                      $generation_id,
891                      $rect,
892                      $field_name);
893
894      $this->fontindex = $fontindex;
895      $this->fontsize  = $fontsize;
896      $this->_value = $value;
897
898//       $this->_appearance = new PDFAppearanceStream($handler,
899//                                                    $handler->_generate_new_object_number(),
900//                                                    $generation_id,
901//                                                    "/Tx BMC EMC");
902    }
903  }
904
905  class PDFFieldMultilineText extends PDFFieldText {
906    function _dict(&$handler) {
907      return array_merge(parent::_dict($handler),
908                         array('Ff'      => PDF_FIELD_TEXT_MULTILINE));
909    }
910  }
911
912  /**
913   * "Password" text input field
914   */
915  class PDFFieldPassword extends PDFFieldText {
916    function PDFFieldPassword(&$handler,
917                              $object_id,
918                              $generation_id,
919                              $rect,
920                              $field_name,
921                              $value,
922                              $fontindex,
923                              $fontsize) {
924      $this->PDFFieldText($handler,
925                          $object_id,
926                          $generation_id,
927                          $rect,
928                          $field_name,
929                          $value,
930                          $fontindex,
931                          $fontsize);
932    }
933
934    function _dict(&$handler) {
935      return array_merge(parent::_dict($handler),
936                         array('Ff'      => PDF_FIELD_TEXT_PASSWORD));
937    }
938  }
939
940  class FPDF {
941    //Private properties
942
943    var $page;               //current page number
944    var $n;                  //current object number
945    var $offsets;            //array of object offsets
946    var $buffer;             //buffer holding in-memory PDF
947    var $pages;              //array containing pages
948    var $state;              //current document state
949    var $compress;           //compression flag
950    var $DefOrientation;     //default orientation
951    var $CurOrientation;     //current orientation
952    var $OrientationChanges; //array indicating orientation changes
953    var $k;                  //scale factor (number of points in user unit)
954    var $fwPt,$fhPt;         //dimensions of page format in points
955    var $fw,$fh;             //dimensions of page format in user unit
956    var $wPt,$hPt;           //current dimensions of page in points
957    var $w,$h;               //current dimensions of page in user unit
958    var $lMargin;            //left margin
959    var $tMargin;            //top margin
960    var $rMargin;            //right margin
961    var $bMargin;            //page break margin
962    var $cMargin;            //cell margin
963    var $x,$y;               //current position in user unit for cell positioning
964    var $lasth;              //height of last cell printed
965    var $LineWidth;          //line width in user unit
966    var $fonts;              //array of used fonts
967    var $FontFiles;          //array of font files
968    var $diffs;              //array of encoding differences
969    var $images;             //array of used images
970    //    var $PageLinks;          //array of links in pages
971    var $links;              //array of internal links
972    var $FontFamily;         //current font family
973    var $FontStyle;          //current font style
974
975    var $underline;          //underlining flag
976    var $overline;
977    var $strikeout;
978
979    var $CurrentFont;        //current font info
980    var $FontSizePt;         //current font size in points
981    var $FontSize;           //current font size in user unit
982    var $DrawColor;          //commands for drawing color
983    var $FillColor;          //commands for filling color
984    var $TextColor;          //commands for text color
985    var $ColorFlag;          //indicates whether fill and text colors are different
986    var $ws;                 //word spacing
987    var $AutoPageBreak;      //automatic page breaking
988    var $PageBreakTrigger;   //threshold used to trigger page breaks
989    var $InFooter;           //flag set when processing footer
990    var $ZoomMode;           //zoom display mode
991    var $LayoutMode;         //layout display mode
992    var $title;              //title
993    var $subject;            //subject
994    var $author;             //author
995    var $keywords;           //keywords
996    var $creator;            //creator
997    var $AliasNbPages;       //alias for total number of pages
998    var $PDFVersion;         //PDF version number
999
1000    var $_forms;
1001    var $_form_radios;
1002    var $_pages;
1003
1004    function moveto($x, $y) {
1005      $this->_out(sprintf("%.2f %.2f m",
1006                          $this->x_coord($x),
1007                          $this->y_coord($y)));
1008    }
1009
1010    function lineto($x, $y) {
1011      $this->_out(sprintf("%.2f %.2f l",
1012                          $this->x_coord($x),
1013                          $this->y_coord($y)));
1014    }
1015
1016    function closepath() {
1017      $this->_out("h");
1018    }
1019
1020    function stroke() {
1021      $this->_out("S");
1022    }
1023
1024    function is_object_written($id) {
1025      return isset($this->offsets[$id]);
1026    }
1027
1028    function x_coord($x) {
1029      return $x * $this->k;
1030    }
1031
1032    function y_coord($y) {
1033      return ($this->h - $y)*$this->k;
1034    }
1035
1036    // PDF specs:
1037    // 3.2.9 Indirect Objects
1038    // Any object in a PDF file may be labeled as an indirect object. This gives the object
1039    // a unique object identifier by which other objects can refer to it (for example, as an
1040    // element of an array or as the value of a dictionary entry). The object identifier
1041    // consists of two parts:
1042    // * A positive integer object number. Indirect objects are often numbered sequentially
1043    //   within a PDF file, but this is not required; object numbers may be
1044    //   assigned in any arbitrary order.
1045    // * A non-negative integer generation number. In a newly created file, all indirect
1046    //   objects have generation numbers of 0. Nonzero generation numbers may be introduced
1047    //   when the file is later updated; see Sections 3.4.3, �Cross-Reference
1048    //   Table,� and 3.4.5, �Incremental Updates.�
1049    // Together, the combination of an object number and a generation number uniquely
1050    // identifies an indirect object. The object retains the same object number and
1051    // generation number throughout its existence, even if its value is modified.
1052    //
1053    function _indirect_object($object) {
1054      $object_number = $object->get_object_id();
1055      $generation_number = $object->get_generation_id();
1056      $object_string = $object->pdf($this);
1057
1058      $this->offsets[$object_number] = strlen($this->buffer);
1059
1060      return "$object_number $generation_number obj\n${object_string}\nendobj";
1061    }
1062
1063    function _stream($content) {
1064      return "stream\n".$content."\nendstream";
1065    }
1066
1067    /**
1068     * @TODO check name for validity
1069     */
1070    function _name($name) {
1071      return sprintf("/%s", $name);
1072    }
1073
1074    function _dictionary($dict) {
1075      $content = "";
1076      foreach ($dict as $key => $value) {
1077        $content .= "/$key $value\n";
1078      };
1079      return "<<\n".$content."\n>>";
1080    }
1081
1082    function _array($array_str) {
1083      return "[$array_str]";
1084    }
1085
1086    function _reference(&$object) {
1087      $object_id     = $object->get_object_id();
1088      $generation_id = $object->get_generation_id();
1089      return "$object_id $generation_id R";
1090    }
1091
1092    function _reference_array($object_array) {
1093      $array_str = "";
1094      for ($i=0; $i<count($object_array); $i++) {
1095        $array_str .= $this->_reference($object_array[$i])." ";
1096      };
1097      return $this->_array($array_str);
1098    }
1099
1100    function _generate_new_object_number() {
1101      $this->n++;
1102      return $this->n;
1103    }
1104
1105    function add_form($name) {
1106      $form = new PDFFieldGroup($this,
1107                                $this->_generate_new_object_number(),    // Object identifier
1108                                0,
1109                                $name);
1110      $this->_forms[] =& $form;
1111    }
1112
1113    function add_field_select($x, $y, $w, $h, $name, $value, $options) {
1114      $field =& new PDFFieldSelect($this,
1115                                   $this->_generate_new_object_number(),    // Object identifier
1116                                   0,                                       // Generation
1117                                   new PDFRect($x, $y, $w, $h),             // Annotation rectangle
1118                                   $name,                                   // Field name
1119                                   $value,
1120                                   $options);
1121
1122      $current_form =& $this->current_form();
1123      $current_form->add_field($field);
1124
1125      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1126    }
1127
1128    /**
1129     * Create new checkbox field object
1130     *
1131     * @param $x Integer Left coordinate of the widget bounding bog
1132     * @param $y Integer Upper coordinate of the widget bounding bog
1133     * @param $w Integer Widget width
1134     * @param $h Integer Widget height
1135     * @param $name String name of the field to be created
1136     * @param $value String value to be posted for this checkbox
1137     *
1138     * @TODO check if fully qualified field name will be unique in PDF file
1139     */
1140    function add_field_checkbox($x, $y, $w, $h, $name, $value, $checked) {
1141      $field =& new PDFFieldCheckBox($this,
1142                                     $this->_generate_new_object_number(),    // Object identifier
1143                                     0,                                       // Generation
1144                                     new PDFRect($x, $y, $w, $h),             // Annotation rectangle
1145                                     $name,                                   // Field name
1146                                     $value, $checked);                                 // Checkbox "on" value
1147
1148      $current_form =& $this->current_form();
1149      $current_form->add_field($field);
1150
1151      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1152    }
1153
1154    function &current_form() {
1155      if (count($this->_forms) == 0) {
1156        /**
1157         * Handle invalid HTML; if we've met an input control outside the form,
1158         * generate a new form with random name
1159         */
1160
1161        $id   = $this->_generate_new_object_number();
1162        $name = sprintf("AnonymousFormObject_%u", $id);
1163
1164        error_log(sprintf("Anonymous form generated with name %s; check your HTML for validity",
1165                          $name));
1166
1167        $form = new PDFFieldGroup($this,
1168                                  $id,    // Object identifier
1169                                  0,
1170                                  $name);
1171        $this->_forms[] =& $form;
1172      };
1173
1174      return $this->_forms[count($this->_forms)-1];
1175    }
1176
1177    function add_field_radio($x, $y, $w, $h, $group_name, $value, $checked) {
1178      if (isset($this->_form_radios[$group_name])) {
1179        $field =& $this->_form_radios[$group_name];
1180      } else {
1181        $field =& new PDFFieldRadioGroup($this,
1182                                         $this->_generate_new_object_number(),
1183                                         0,
1184                                         $group_name);
1185
1186        $current_form =& $this->current_form();
1187        $current_form->add_field($field);
1188
1189        $this->_form_radios[$group_name] =& $field;
1190      };
1191
1192      $radio =& new PDFFieldRadio($this,
1193                                  $this->_generate_new_object_number(),
1194                                  0,
1195                                  new PDFRect($x, $y, $w, $h),
1196                                  $value);
1197      $field->add_field($radio);
1198      if ($checked) { $field->set_checked($value); };
1199
1200      $this->_pages[count($this->_pages)-1]->add_annotation($radio);
1201    }
1202
1203    /**
1204     * Create a new interactive text form
1205     *
1206     * @param $x Left coordinate of the widget bounding box
1207     * @param $y Top coordinate of the widget bounding box
1208     * @param $w Widget width
1209     * @param $h Widget height
1210     * @param $value Default widget value
1211     * @param $field_name Field name
1212     *
1213     * @return Field number
1214     */
1215    function add_field_text($x, $y, $w, $h, $value, $field_name) {
1216      $field =& new PDFFieldText($this,
1217                                 $this->_generate_new_object_number(),
1218                                 0,
1219                                 new PDFRect($x, $y, $w, $h),
1220                                 $field_name,
1221                                 $value,
1222                                 $this->CurrentFont['i'],
1223                                 $this->FontSizePt);
1224
1225      $current_form =& $this->current_form();
1226      $current_form->add_field($field);
1227
1228      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1229    }
1230
1231    function add_field_multiline_text($x, $y, $w, $h, $value, $field_name) {
1232      $field =& new PDFFieldMultilineText($this,
1233                                          $this->_generate_new_object_number(),
1234                                          0,
1235                                          new PDFRect($x, $y, $w, $h),
1236                                          $field_name,
1237                                          $value,
1238                                          $this->CurrentFont['i'],
1239                                          $this->FontSizePt);
1240
1241      $current_form =& $this->current_form();
1242      $current_form->add_field($field);
1243
1244      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1245    }
1246
1247    /**
1248     * Create a new interactive password input field
1249     *
1250     * @param $x Left coordinate of the widget bounding box
1251     * @param $y Top coordinate of the widget bounding box
1252     * @param $w Widget width
1253     * @param $h Widget height
1254     * @param $value Default widget value
1255     * @param $field_name Field name
1256     *
1257     * @return Field number
1258     */
1259    function add_field_password($x, $y, $w, $h, $value, $field_name) {
1260      $field =& new PDFFieldPassword($this,
1261                                     $this->_generate_new_object_number(),
1262                                     0,
1263                                     new PDFRect($x, $y, $w, $h),
1264                                     $field_name,
1265                                     $value,
1266                                     $this->CurrentFont['i'],
1267                                     $this->FontSizePt);
1268
1269      $current_form =& $this->current_form();
1270      $current_form->add_field($field);
1271
1272      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1273    }
1274
1275    function add_field_pushbuttonimage($x, $y, $w, $h, $field_name, $value, $actionURL) {
1276      $field =& new PDFFieldPushButtonImage($this,
1277                                            $this->_generate_new_object_number(),
1278                                            0,
1279                                            new PDFRect($x, $y, $w, $h),
1280                                            $this->CurrentFont['i'],
1281                                            $this->FontSizePt,
1282                                            $field_name,
1283                                            $value,
1284                                            $actionURL);
1285
1286      $current_form =& $this->current_form();
1287      $current_form->add_field($field);
1288
1289      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1290    }
1291
1292    function add_field_pushbuttonsubmit($x, $y, $w, $h, $field_name, $value, $actionURL) {
1293      $field =& new PDFFieldPushButtonSubmit($this,
1294                                             $this->_generate_new_object_number(),
1295                                             0,
1296                                             new PDFRect($x, $y, $w, $h),
1297                                             $this->CurrentFont['i'],
1298                                             $this->FontSizePt,
1299                                             $field_name,
1300                                             $value,
1301                                             $actionURL);
1302
1303      $current_form =& $this->current_form();
1304      $current_form->add_field($field);
1305
1306      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1307    }
1308
1309    function add_field_pushbuttonreset($x, $y, $w, $h) {
1310      $field =& new PDFFieldPushButtonReset($this,
1311                                            $this->_generate_new_object_number(),
1312                                            0,
1313                                            new PDFRect($x, $y, $w, $h),
1314                                            null,
1315                                            $this->CurrentFont['i'],
1316                                            $this->FontSizePt);
1317
1318      $current_form =& $this->current_form();
1319      $current_form->add_field($field);
1320
1321      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1322    }
1323
1324    function add_field_pushbutton($x, $y, $w, $h) {
1325      $field =& new PDFFieldPushButton($this,
1326                                       $this->_generate_new_object_number(),
1327                                       0,
1328                                       new PDFRect($x, $y, $w, $h),
1329                                       null,
1330                                       $this->CurrentFont['i'],
1331                                       $this->FontSizePt);
1332
1333      $current_form =& $this->current_form();
1334      $current_form->add_field($field);
1335
1336      $this->_pages[count($this->_pages)-1]->add_annotation($field);
1337    }
1338
1339
1340    function SetDash($x, $y) {
1341      $x = (int)$x;
1342      $y = (int)$y;
1343      $this->_out(sprintf("[%d %d] 0 d", $x*2, $y*2));
1344    }
1345
1346    function _GetFontBBox() {
1347      return preg_split("/[\[\]\s]+/", $this->CurrentFont['desc']['FontBBox']);
1348    }
1349
1350    function _dounderline($x,$y,$txt) {
1351      //Underline text
1352      $up=$this->CurrentFont['up'];
1353      $ut=$this->CurrentFont['ut'];
1354      $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
1355      return sprintf('%.2f %.2f %.2f %.2f re f',
1356                     $x*$this->k,
1357                     ($this->h-($y-$up/1000*$this->FontSize))*$this->k,
1358                     $w*$this->k,
1359                     -$ut/1000*$this->FontSizePt);
1360    }
1361
1362    function _dooverline($x,$y,$txt) {
1363      $bbox = $this->_GetFontBBox();
1364      $up = round($bbox[3] * 0.8);
1365
1366      $ut=$this->CurrentFont['ut'];
1367
1368      $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
1369      return sprintf('%.2f %.2f %.2f %.2f re f',
1370                     $x*$this->k,
1371                     ($this->h-($y-$up/1000*$this->FontSize))*$this->k,
1372                     $w*$this->k,
1373                     -$ut/1000*$this->FontSizePt);
1374    }
1375
1376    function _dostrikeout($x,$y,$txt) {
1377      $bbox = $this->_GetFontBBox();
1378      $up = round($bbox[3] * 0.25);
1379
1380      $ut=$this->CurrentFont['ut'];
1381      $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
1382      return sprintf('%.2f %.2f %.2f %.2f re f',
1383                     $x*$this->k,
1384                     ($this->h-($y-$up/1000*$this->FontSize))*$this->k,
1385                     $w*$this->k,
1386                     -$ut/1000*$this->FontSizePt);
1387    }
1388
1389    function SetDecoration($underline, $overline, $strikeout) {
1390      $this->underline = $underline;
1391      $this->overline  = $overline;
1392      $this->strikeout = $strikeout;
1393    }
1394
1395    function ClipPath($path) {
1396      if (count($path) < 3) {
1397        die("Attempt to clip on the path containing less than three points");
1398      };
1399
1400      $this->MakePath($path);
1401      $this->Clip();
1402    }
1403
1404    function Clip() {
1405      $this->_out("W n");
1406    }
1407
1408    function _LoadFont($fontkey, $family, $encoding, $style) {
1409      $style  = strtolower($style);
1410
1411      if (!isset($this->fonts[$fontkey])) {
1412        global $g_font_resolver_pdf;
1413        $file = $g_font_resolver_pdf->ttf_mappings[$family];
1414
1415        $embed = $g_font_resolver_pdf->embed[$family];
1416
1417        // Remove the '.ttf' suffix
1418        $file = substr($file, 0, strlen($file) - 4);
1419
1420        // Generate (if required) PHP font description files
1421        if (!file_exists($this->_getfontpath().$fontkey.'.php')) {
1422          // As MakeFont squeaks a lot, we'll need to capture and discard its output
1423          ob_start();
1424          MakeFont(PDFLIB_TTF_FONTS_REPOSITORY.$file.'.ttf',
1425                   PDFLIB_TTF_FONTS_REPOSITORY.$file.'.afm',
1426                   $this->_getfontpath(),
1427                   $fontkey.'.php',
1428                   $encoding);
1429          ob_end_clean();
1430        };
1431
1432        $this->AddFont($fontkey, $family, "", $encoding, $fontkey.'.php', $embed);
1433      };
1434    }
1435
1436    function _MakeFontKey($family, $encoding) {
1437      return $family.'-'.$encoding;
1438    }
1439
1440    function GetFontAscender($name, $encoding) {
1441      $fontkey = $this->_MakeFontKey($name, $encoding);
1442      $this->_LoadFont($fontkey, $name, $encoding, '');
1443      return $this->fonts[$fontkey]['desc']['Ascent'] / 1000;
1444    }
1445
1446    function GetFontDescender($name, $encoding) {
1447      $fontkey = $this->_MakeFontKey($name, $encoding);
1448      $this->_LoadFont($fontkey, $name, $encoding, '');
1449      return -$this->fonts[$fontkey]['desc']['Descent'] / 1000;
1450    }
1451
1452    // Note that FPDF do some caching, which can conflict with "save/restore" pairs
1453    function Save() { $this->_out("q"); }
1454    function Restore() { $this->_out("Q"); }
1455
1456    function Translate($dx, $dy) {
1457      $this->_out(sprintf("1 0 0 1 %.2f %.2f cm", $dx, $dy));
1458    }
1459
1460    function Rotate($alpha) {
1461      $this->_out(sprintf("%.2f %.2f %.2f %.2f 0 0 cm",
1462                          cos($alpha/180*pi()),
1463                          sin($alpha/180*pi()),
1464                          -sin($alpha/180*pi()),
1465                          cos($alpha/180*pi())
1466                          ));
1467    }
1468
1469    function SetTextRendering($mode) {
1470      $this->_out(sprintf("%d Tr", $mode));
1471    }
1472
1473    function MakePath($path) {
1474      $this->_out(sprintf("%.2f %.2f m", $path[0]['x'], $path[0]['y']));
1475
1476      for ($i=1; $i<count($path); $i++) {
1477        $this->_out(sprintf("%.2f %.2f l", $path[$i]['x'], $path[$i]['y']));
1478      };
1479    }
1480
1481    function FillPath($path) {
1482      if (count($path) < 3) {
1483        die("Attempt to fill path containing less than three points");
1484      };
1485
1486      $this->_out($this->FillColor);
1487      $this->MakePath($path);
1488      $this->Fill();
1489    }
1490
1491    function Fill() {
1492      $this->_out("f");
1493    }
1494
1495    // Thanks G. Adam Stanislav for information about approximation circle using bezier curves
1496    // http://www.whizkidtech.redprince.net/bezier/circle/
1497    //
1498    function Circle($x, $y, $r) {
1499      $kappa = (sqrt(2) - 1) / 3 * 4;
1500      $l = $kappa * $r;
1501
1502      $this->_out(sprintf("%.2f %.f2 m", $x + $r, $y));
1503      $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
1504                          $x + $r, $y + $l,
1505                          $x + $l, $y + $r,
1506                          $x, $y + $r));
1507      $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
1508                          $x - $l, $y + $r,
1509                          $x - $r, $y + $l,
1510                          $x - $r, $y));
1511      $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
1512                          $x - $r, $y - $l,
1513                          $x - $l, $y - $r,
1514                          $x, $y - $r));
1515      $this->_out(sprintf("%.2f %.f2 %.2f %.2f %.2f %.2f c",
1516                          $x + $l, $y - $r,
1517                          $x + $r, $y - $l,
1518                          $x + $r, $y));
1519    }
1520
1521    /*******************************************************************************
1522     *                                                                              *
1523     *                               Public methods                                 *
1524     *                                                                              *
1525     *******************************************************************************/
1526    function FPDF($orientation='P',$unit='mm',$format='A4') {
1527      $this->_forms = array();
1528      $this->_form_radios = array();
1529      $this->_pages = array();
1530
1531      //Some checks
1532      $this->_dochecks();
1533
1534      //Initialization of properties
1535      $this->page=0;
1536
1537      $this->n=2;
1538
1539      $this->buffer='';
1540      $this->pages=array();
1541      $this->OrientationChanges=array();
1542      $this->state=0;
1543      $this->fonts=array();
1544      $this->FontFiles=array();
1545      $this->diffs  = array();
1546      $this->images = array();
1547      $this->links  = array();
1548      $this->InFooter=false;
1549      $this->lasth=0;
1550      $this->FontFamily='';
1551      $this->FontStyle='';
1552      $this->FontSizePt=12;
1553
1554      $this->underline = false;
1555      $this->overline  = false;
1556      $this->strikeout = false;
1557
1558      $this->DrawColor='0 G';
1559      $this->FillColor='0 g';
1560      $this->TextColor='0 g';
1561      $this->ColorFlag=false;
1562      $this->ws=0;
1563
1564      //Scale factor
1565      switch ($unit) {
1566      case 'pt':
1567        $this->k = 1; break;
1568      case 'mm':
1569        $this->k = 72/25.4; break;
1570      case 'cm':
1571        $this->k = 72/2.54; break;
1572      case 'in':
1573        $this->k = 72;
1574      default:
1575        $this->Error('Incorrect unit: '.$unit);
1576      };
1577
1578      //Page format
1579      if (is_string($format)) {
1580        $format=strtolower($format);
1581
1582        switch ($format) {
1583        case 'a3':
1584          $format=array(841.89,1190.55); break;
1585        case 'a4':
1586          $format=array(595.28,841.89); break;
1587        case 'a5':
1588          $format=array(420.94,595.28); break;
1589        case 'letter':
1590          $format=array(612,792); break;
1591        case 'legal':
1592          $format=array(612,1008); break;
1593        default:
1594          $this->Error('Unknown page format: '.$format);
1595        };
1596        $this->fwPt=$format[0];
1597        $this->fhPt=$format[1];
1598      } else {
1599        $this->fwPt=$format[0]*$this->k;
1600        $this->fhPt=$format[1]*$this->k;
1601      };
1602
1603      $this->fw=$this->fwPt/$this->k;
1604      $this->fh=$this->fhPt/$this->k;
1605
1606      //Page orientation
1607      $orientation=strtolower($orientation);
1608      if($orientation=='p' || $orientation=='portrait') {
1609        $this->DefOrientation='P';
1610        $this->wPt=$this->fwPt;
1611        $this->hPt=$this->fhPt;
1612      } elseif($orientation=='l' || $orientation=='landscape') {
1613        $this->DefOrientation='L';
1614        $this->wPt=$this->fhPt;
1615        $this->hPt=$this->fwPt;
1616      } else {
1617        $this->Error('Incorrect orientation: '.$orientation);
1618      };
1619
1620      $this->CurOrientation=$this->DefOrientation;
1621      $this->w=$this->wPt/$this->k;
1622      $this->h=$this->hPt/$this->k;
1623      //Page margins (1 cm)
1624      $margin=28.35/$this->k;
1625      $this->SetMargins($margin,$margin);
1626      //Interior cell margin (1 mm)
1627      $this->cMargin=$margin/10;
1628      //Line width (0.2 mm)
1629      $this->LineWidth=.567/$this->k;
1630      //Automatic page break
1631      $this->SetAutoPageBreak(true,2*$margin);
1632
1633      //Full width display mode
1634      $this->SetDisplayMode('fullwidth');
1635
1636      //Enable compression
1637      $this->SetCompression(true);
1638
1639      //Set default PDF version number
1640      $this->PDFVersion='1.3';
1641    }
1642
1643    function SetMargins($left,$top,$right=-1)
1644    {
1645      //Set left, top and right margins
1646      $this->lMargin=$left;
1647      $this->tMargin=$top;
1648      if($right==-1)
1649        $right=$left;
1650      $this->rMargin=$right;
1651    }
1652
1653    function SetLeftMargin($margin)
1654    {
1655      //Set left margin
1656      $this->lMargin=$margin;
1657      if($this->page>0 && $this->x<$margin)
1658        $this->x=$margin;
1659    }
1660
1661    function SetTopMargin($margin)
1662    {
1663      //Set top margin
1664      $this->tMargin=$margin;
1665    }
1666
1667    function SetRightMargin($margin)
1668    {
1669      //Set right margin
1670      $this->rMargin=$margin;
1671    }
1672
1673    function SetAutoPageBreak($auto,$margin=0)
1674    {
1675      //Set auto page break mode and triggering margin
1676      $this->AutoPageBreak=$auto;
1677      $this->bMargin=$margin;
1678      $this->PageBreakTrigger=$this->h-$margin;
1679    }
1680
1681    function SetDisplayMode($zoom,$layout='continuous')
1682    {
1683      //Set display mode in viewer
1684      if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
1685        $this->ZoomMode=$zoom;
1686      else
1687        $this->Error('Incorrect zoom display mode: '.$zoom);
1688      if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
1689        $this->LayoutMode=$layout;
1690      else
1691        $this->Error('Incorrect layout display mode: '.$layout);
1692    }
1693
1694    function SetCompression($compress)
1695    {
1696      //Set page compression
1697      if(function_exists('gzcompress'))
1698        $this->compress=$compress;
1699      else
1700        $this->compress=false;
1701    }
1702
1703    function SetTitle($title)
1704    {
1705      //Title of document
1706      $this->title=$title;
1707    }
1708
1709    function SetSubject($subject)
1710    {
1711      //Subject of document
1712      $this->subject=$subject;
1713    }
1714
1715    function SetAuthor($author)
1716    {
1717      //Author of document
1718      $this->author=$author;
1719    }
1720
1721    function SetKeywords($keywords)
1722    {
1723      //Keywords of document
1724      $this->keywords=$keywords;
1725    }
1726
1727    function SetCreator($creator)
1728    {
1729      //Creator of document
1730      $this->creator=$creator;
1731    }
1732
1733    function AliasNbPages($alias='{nb}')
1734    {
1735      //Define an alias for total number of pages
1736      $this->AliasNbPages=$alias;
1737    }
1738
1739    function Error($msg)
1740    {
1741      //Fatal error
1742      die('<B>FPDF error: </B>'.$msg);
1743    }
1744
1745    function Open() {
1746      //Begin document
1747      $this->state=1;
1748    }
1749
1750    function Close() {
1751      //Terminate document
1752      if ($this->state==3) {
1753        return;
1754      };
1755
1756      if ($this->page==0) {
1757        $this->AddPage();
1758      };
1759
1760      //Page footer
1761      $this->InFooter=true;
1762      $this->Footer();
1763      $this->InFooter=false;
1764      //Close page
1765      $this->_endpage();
1766      //Close document
1767      $this->_enddoc();
1768    }
1769
1770    function AddPage($orientation='') {
1771      $this->_pages[] =& new PDFPage($this, $this->_generate_new_object_number(), 0);
1772
1773      //Start a new page
1774      if ($this->state==0) {
1775        $this->Open();
1776      };
1777
1778      $family=$this->FontFamily;
1779
1780      $style=$this->FontStyle.($this->underline ? 'U' : '');
1781
1782      $size=$this->FontSizePt;
1783      $lw=$this->LineWidth;
1784      $dc=$this->DrawColor;
1785      $fc=$this->FillColor;
1786      $tc=$this->TextColor;
1787      $cf=$this->ColorFlag;
1788      if ($this->page>0) {
1789        //Page footer
1790        $this->InFooter=true;
1791        $this->Footer();
1792        $this->InFooter=false;
1793        //Close page
1794        $this->_endpage();
1795      }
1796
1797      //Start new page
1798      $this->_beginpage($orientation);
1799      //Set line cap style to square
1800      $this->_out('2 J');
1801      //Set line width
1802      $this->LineWidth=$lw;
1803      $this->_out(sprintf('%.2f w',$lw*$this->k));
1804
1805      //Set colors
1806      $this->DrawColor=$dc;
1807      if ($dc!='0 G') {
1808        $this->_out($dc);
1809      };
1810
1811      $this->FillColor=$fc;
1812      if ($fc!='0 g') {
1813        $this->_out($fc);
1814      };
1815
1816      $this->TextColor=$tc;
1817      $this->ColorFlag=$cf;
1818      //Page header
1819      $this->Header();
1820
1821      //Restore line width
1822      if ($this->LineWidth!=$lw) {
1823        $this->LineWidth=$lw;
1824        $this->_out(sprintf('%.2f w',$lw*$this->k));
1825      }
1826
1827      //Restore colors
1828      if ($this->DrawColor!=$dc) {
1829        $this->DrawColor=$dc;
1830        $this->_out($dc);
1831      }
1832      if ($this->FillColor!=$fc) {
1833        $this->FillColor=$fc;
1834        $this->_out($fc);
1835      }
1836      $this->TextColor=$tc;
1837      $this->ColorFlag=$cf;
1838
1839      if (!is_null($this->CurrentFont)) {
1840        $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
1841      };
1842    }
1843
1844    function Header()
1845    {
1846      //To be implemented in your own inherited class
1847    }
1848
1849    function Footer()
1850    {
1851      //To be implemented in your own inherited class
1852    }
1853
1854    function PageNo()
1855    {
1856      //Get current page number
1857      return $this->page;
1858    }
1859
1860    function SetDrawColor($r,$g=-1,$b=-1) {
1861      // Set color for all stroking operations
1862      if (($r==0 && $g==0 && $b==0) || $g==-1) {
1863        $new_color = sprintf('%.3f G',$r/255);
1864      } else {
1865        $new_color = sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255);
1866      };
1867
1868      if ($this->page > 0 /*&& $this->DrawColor != $new_color*/) {
1869        $this->DrawColor = $new_color;
1870        $this->_out($this->DrawColor);
1871      };
1872    }
1873
1874    function SetFillColor($r,$g=-1,$b=-1) {
1875      // Set color for all filling operations
1876      if (($r==0 && $g==0 && $b==0) || $g==-1) {
1877        $new_color = sprintf('%.3f g',$r/255);
1878      } else {
1879        $new_color = sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
1880      };
1881
1882      if ($this->page>0 /*&& $this->FillColor != $new_color*/) {
1883        $this->FillColor = $new_color;
1884        $this->ColorFlag=($this->FillColor!=$this->TextColor);
1885        $this->_out($this->FillColor);
1886      };
1887    }
1888
1889    function SetTextColor($r,$g=-1,$b=-1) {
1890      //Set color for text
1891      if (($r==0 && $g==0 && $b==0) || $g==-1) {
1892        $this->TextColor=sprintf('%.3f g',$r/255);
1893      } else {
1894        $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
1895      };
1896
1897      $this->ColorFlag=($this->FillColor!=$this->TextColor);
1898    }
1899
1900    function GetStringWidth($s) {
1901      //Get width of a string in the current font
1902      $s=(string)$s;
1903      $cw = &$this->CurrentFont['cw'];
1904      $w=0;
1905
1906      $l=strlen($s);
1907      for($i=0; $i<$l; $i++) {
1908        $w+=$cw[$s{$i}];
1909      };
1910
1911      return $w*$this->FontSize/1000;
1912    }
1913
1914    function SetLineWidth($width) {
1915      //Set line width
1916      $this->LineWidth=$width;
1917      if($this->page>0)
1918        $this->_out(sprintf('%.2f w',$width*$this->k));
1919    }
1920
1921    function Line($x1,$y1,$x2,$y2) {
1922      //Draw a line
1923      $this->_out(sprintf('%.2f %.2f m %.2f %.2f l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));
1924    }
1925
1926    function Rect($x,$y,$w,$h,$style='')
1927    {
1928      //Draw a rectangle
1929      if($style=='F')
1930        $op='f';
1931      elseif($style=='FD' || $style=='DF')
1932        $op='B';
1933      else
1934        $op='S';
1935      $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
1936    }
1937
1938    function AddFont($fontkey, $family, $style, $encoding, $file, $bEmbed) {
1939      //Add a TrueType or Type1 font
1940      if ($file=='') {
1941        $file=str_replace(' ','',$family).strtolower($style).'.php';
1942      };
1943
1944      $style=strtoupper($style);
1945      if ($style=='IB') { $style='BI'; };
1946
1947      if(isset($this->fonts[$fontkey])) {
1948        $this->Error('Font already added: '.$family.' '.$style);
1949      };
1950
1951      $filepath = $this->_getfontpath().$file;
1952      include($filepath);
1953
1954      // After we've executed 'include' the $file variable
1955      // have been overwritten by $file declared in font definition file; if we do not want
1956      // to embed the font in the PDF file, we should set to empty string
1957      if (!$bEmbed) { $file = ''; };
1958
1959      if(!isset($name)) {
1960        $this->Error("Could not include font definition file: $filepath");
1961      };
1962
1963      $i=count($this->fonts)+1;
1964      $this->fonts[$fontkey]=array('i'    =>$i,
1965                                   'type' =>$type,
1966                                   'name' =>$name,
1967                                   'desc' =>$desc,
1968                                   'up'   =>$up,
1969                                   'ut'   =>$ut,
1970                                   'cw'   =>$cw,
1971                                   'enc'  =>$enc,
1972                                   'file' =>$file);
1973
1974      if ($diff) {
1975        //Search existing encodings
1976        $d=0;
1977        $nb=count($this->diffs);
1978        for ($i=1; $i<=$nb; $i++) {
1979          if($this->diffs[$i]==$diff)
1980            {
1981              $d=$i;
1982              break;
1983            }
1984        }
1985        if ($d==0) {
1986          $d=$nb+1;
1987          $this->diffs[$d]=$diff;
1988        }
1989        $this->fonts[$fontkey]['diff']=$d;
1990      }
1991
1992      if ($file) {
1993        if($type=='TrueType') {
1994          $this->FontFiles[$file]=array('length1'=>$originalsize);
1995        } else {
1996          $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2);
1997        };
1998      }
1999    }
2000
2001    function SetFont($family,$encoding, $style='',$size=0) {
2002      // Select a font; size given in points
2003      global $fpdf_charwidths;
2004
2005      $style=strtoupper($style);
2006      if (strpos($style,'U')!==false) {
2007        $this->underline=true;
2008        $style=str_replace('U','',$style);
2009      } else {
2010        $this->underline=false;
2011      };
2012      if ($style=='IB') { $style='BI'; };
2013
2014      if ($size==0) {
2015        $size = $this->FontSizePt;
2016      };
2017
2018      $fontkey = $this->_MakeFontKey($family, $encoding);
2019      $this->_LoadFont($fontkey, $family, $encoding, $style);
2020
2021      if ($this->page > 0 /* &&
2022          ($this->CurrentFont['i'] != $this->fonts[$fontkey]['i'] ||
2023           $this->FontSizePt != $size) */) {
2024        //Select it
2025        $this->FontFamily  = $family;
2026        $this->FontStyle   = $style;
2027        $this->FontSizePt  = $size;
2028        $this->FontSize    = $size/$this->k;
2029
2030        $this->CurrentFont = &$this->fonts[$fontkey];
2031        $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
2032      };
2033    }
2034
2035//     function SetFontSize($size) {
2036//       //Set font size in points
2037//       if ($this->FontSizePt == $size) {
2038//         return;
2039//       };
2040
2041//       $this->FontSizePt=$size;
2042//       $this->FontSize=$size/$this->k;
2043//       if ($this->page>0) {
2044//         $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
2045//       };
2046//     }
2047
2048    function AddLink() {
2049      //Create a new internal link
2050      $n=count($this->links)+1;
2051      $this->links[$n]=array(0,0);
2052      return $n;
2053    }
2054
2055    function SetLink($link,$y=0,$page=-1) {
2056      //Set destination of internal link
2057      if ($y==-1) {
2058        $y=$this->y;
2059      };
2060      if ($page==-1) {
2061        $page=$this->page;
2062      };
2063      $this->links[$link]=array($page,$y);
2064    }
2065
2066    /**
2067     * Add an external hyperlink on the page (an rectangular area). It is not bound to any other PDF element,
2068     * like text. It is the task of layout engine to draw the appropriate text inside this area.
2069     *
2070     * @param Float $x X-coordinate of the upper-left corner of the link area
2071     * @param Float $y Y-coordinate of the upper-left corner of the link area
2072     * @param Float $w link area width
2073     * @param Float $h link area height
2074     * @param String $link Link URL
2075     */
2076    function add_link_external($x, $y, $w, $h, $link) {
2077      $link = new PDFAnnotationExternalLink($this,
2078                                            $this->_generate_new_object_number(),
2079                                            0,
2080                                            new PDFRect($x, $y, $w, $h),
2081                                            $link);
2082      $this->_pages[count($this->_pages)-1]->add_annotation($link);
2083    }
2084
2085    /**
2086     * Add an internal hyperlink on the page (an rectangular area). It is not bound to any other PDF element,
2087     * like text. It is the task of layout engine to draw the appropriate text inside this area.
2088     *
2089     * @param Float $x X-coordinate of the upper-left corner of the link area
2090     * @param Float $y Y-coordinate of the upper-left corner of the link area
2091     * @param Float $w link area width
2092     * @param Float $h link area height
2093     * @param Integer $link Internal Link identifier
2094     */
2095    function add_link_internal($x, $y, $w, $h, $link) {
2096      $link = new PDFAnnotationInternalLink($this,
2097                                            $this->_generate_new_object_number(),
2098                                            0,
2099                                            new PDFRect($x, $y, $w, $h),
2100                                            $link);
2101      $this->_pages[count($this->_pages)-1]->add_annotation($link);
2102    }
2103
2104    function Text($x,$y,$txt) {
2105      //Output a string
2106      $s=sprintf('BT %.2f %.2f Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));
2107
2108      if ($this->underline && $txt!='') {
2109        $s.=' '.$this->_dounderline($x,$y,$txt);
2110      }
2111
2112      if ($this->overline && $txt!='') {
2113        $s.=' '.$this->_dooverline($x,$y,$txt);
2114      }
2115
2116      if ($this->strikeout && $txt!='') {
2117        $s.=' '.$this->_dostrikeout($x,$y,$txt);
2118      }
2119
2120      if($this->ColorFlag)
2121        $s='q '.$this->TextColor.' '.$s.' Q';
2122      $this->_out($s);
2123    }
2124
2125    function AcceptPageBreak()
2126    {
2127      //Accept automatic page break or not
2128      return $this->AutoPageBreak;
2129    }
2130
2131    function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='')
2132    {
2133      //Output a cell
2134      $k=$this->k;
2135      if($this->y+$h>$this->PageBreakTrigger && !$this->InFooter && $this->AcceptPageBreak())
2136	{
2137          //Automatic page break
2138          $x=$this->x;
2139          $ws=$this->ws;
2140          if($ws>0)
2141            {
2142              $this->ws=0;
2143              $this->_out('0 Tw');
2144            }
2145          $this->AddPage($this->CurOrientation);
2146          $this->x=$x;
2147          if($ws>0)
2148            {
2149              $this->ws=$ws;
2150              $this->_out(sprintf('%.3f Tw',$ws*$k));
2151            }
2152	}
2153      if($w==0)
2154        $w=$this->w-$this->rMargin-$this->x;
2155      $s='';
2156      if($fill==1 || $border==1)
2157	{
2158          if($fill==1)
2159            $op=($border==1) ? 'B' : 'f';
2160          else
2161            $op='S';
2162          $s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
2163	}
2164      if(is_string($border))
2165	{
2166          $x=$this->x;
2167          $y=$this->y;
2168          if(strpos($border,'L')!==false)
2169            $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
2170          if(strpos($border,'T')!==false)
2171            $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
2172          if(strpos($border,'R')!==false)
2173            $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
2174          if(strpos($border,'B')!==false)
2175            $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
2176	}
2177      if($txt!=='')
2178	{
2179          if ($align=='R') {
2180            $dx=$w-$this->cMargin-$this->GetStringWidth($txt);
2181          } elseif ($align=='C') {
2182            $dx=($w-$this->GetStringWidth($txt))/2;
2183          } else {
2184            $dx=$this->cMargin;
2185          };
2186
2187          if ($this->ColorFlag) {
2188            $s.='q '.$this->TextColor.' ';
2189          };
2190
2191          $txt2=str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt)));
2192          $s.=sprintf('BT %.2f %.2f Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
2193
2194          if ($this->underline) {
2195            $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
2196          };
2197
2198          if($this->ColorFlag)
2199            $s.=' Q';
2200          if($link)
2201            $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
2202	}
2203      if($s)
2204        $this->_out($s);
2205      $this->lasth=$h;
2206      if($ln>0)
2207	{
2208          //Go to next line
2209          $this->y+=$h;
2210          if($ln==1)
2211            $this->x=$this->lMargin;
2212	}
2213      else
2214        $this->x+=$w;
2215    }
2216
2217    function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0)
2218    {
2219      //Output text with automatic or explicit line breaks
2220      $cw=&$this->CurrentFont['cw'];
2221      if($w==0)
2222        $w=$this->w-$this->rMargin-$this->x;
2223      $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
2224      $s=str_replace("\r",'',$txt);
2225      $nb=strlen($s);
2226      if($nb>0 && $s[$nb-1]=="\n")
2227        $nb--;
2228      $b=0;
2229      if($border)
2230	{
2231          if($border==1)
2232            {
2233              $border='LTRB';
2234              $b='LRT';
2235              $b2='LR';
2236            }
2237          else
2238            {
2239              $b2='';
2240              if(strpos($border,'L')!==false)
2241                $b2.='L';
2242              if(strpos($border,'R')!==false)
2243                $b2.='R';
2244              $b=(strpos($border,'T')!==false) ? $b2.'T' : $b2;
2245            }
2246	}
2247      $sep=-1;
2248      $i=0;
2249      $j=0;
2250      $l=0;
2251      $ns=0;
2252      $nl=1;
2253      while($i<$nb)
2254	{
2255          //Get next character
2256          $c=$s{$i};
2257          if($c=="\n")
2258            {
2259              //Explicit line break
2260              if($this->ws>0)
2261                {
2262                  $this->ws=0;
2263                  $this->_out('0 Tw');
2264                }
2265              $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
2266              $i++;
2267              $sep=-1;
2268              $j=$i;
2269              $l=0;
2270              $ns=0;
2271              $nl++;
2272              if($border && $nl==2)
2273                $b=$b2;
2274              continue;
2275            }
2276          if($c==' ')
2277            {
2278              $sep=$i;
2279              $ls=$l;
2280              $ns++;
2281            }
2282          $l+=$cw[$c];
2283          if($l>$wmax)
2284            {
2285              //Automatic line break
2286              if($sep==-1)
2287                {
2288                  if($i==$j)
2289                    $i++;
2290                  if($this->ws>0)
2291                    {
2292                      $this->ws=0;
2293                      $this->_out('0 Tw');
2294                    }
2295                  $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
2296                }
2297              else
2298                {
2299                  if($align=='J')
2300                    {
2301                      $this->ws=($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
2302                      $this->_out(sprintf('%.3f Tw',$this->ws*$this->k));
2303                    }
2304                  $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
2305                  $i=$sep+1;
2306                }
2307              $sep=-1;
2308              $j=$i;
2309              $l=0;
2310              $ns=0;
2311              $nl++;
2312              if($border && $nl==2)
2313                $b=$b2;
2314            }
2315          else
2316            $i++;
2317	}
2318      //Last chunk
2319      if($this->ws>0)
2320	{
2321          $this->ws=0;
2322          $this->_out('0 Tw');
2323	}
2324      if($border && strpos($border,'B')!==false)
2325        $b.='B';
2326      $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
2327      $this->x=$this->lMargin;
2328    }
2329
2330    function Write($h, $txt, $link='') {
2331      //Output text in flowing mode
2332      $cw=&$this->CurrentFont['cw'];
2333      $w=$this->w-$this->rMargin-$this->x;
2334      $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
2335      $s=str_replace("\r",'',$txt);
2336      $nb=strlen($s);
2337      $sep=-1;
2338      $i=0;
2339      $j=0;
2340      $l=0;
2341      $nl=1;
2342      while ($i<$nb) {
2343        //Get next character
2344        $c=$s{$i};
2345        if ($c=="\n") {
2346          //Explicit line break
2347          $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
2348          $i++;
2349          $sep=-1;
2350          $j=$i;
2351          $l=0;
2352          if ($nl==1) {
2353            $this->x=$this->lMargin;
2354            $w=$this->w-$this->rMargin-$this->x;
2355            $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
2356          }
2357          $nl++;
2358          continue;
2359        }
2360        if ($c==' ') {
2361          $sep=$i;
2362        };
2363        $l+=$cw[$c];
2364        if ($l>$wmax) {
2365          //Automatic line break
2366          if ($sep==-1) {
2367            if($this->x>$this->lMargin) {
2368              //Move to next line
2369              $this->x=$this->lMargin;
2370              $this->y+=$h;
2371              $w=$this->w-$this->rMargin-$this->x;
2372              $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
2373              $i++;
2374              $nl++;
2375              continue;
2376            }
2377            if ($i==$j) {
2378              $i++;
2379            };
2380            $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
2381          } else {
2382            $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);
2383            $i=$sep+1;
2384          }
2385          $sep=-1;
2386          $j=$i;
2387          $l=0;
2388          if ($nl==1) {
2389            $this->x=$this->lMargin;
2390                  $w=$this->w-$this->rMargin-$this->x;
2391                  $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
2392                }
2393              $nl++;
2394            }
2395          else
2396            $i++;
2397	}
2398      //Last chunk
2399      if($i!=$j)
2400        $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',0,$link);
2401    }
2402
2403    function Image($file,$x,$y,$w=0,$h=0,$type='',$link='')
2404    {
2405      //Put an image on the page
2406      if(!isset($this->images[$file]))
2407	{
2408          //First use of image, get info
2409          if($type=='')
2410            {
2411              $pos=strrpos($file,'.');
2412              if(!$pos)
2413                $this->Error('Image file has no extension and no type was specified: '.$file);
2414              $type=substr($file,$pos+1);
2415            }
2416          $type=strtolower($type);
2417          $mqr=get_magic_quotes_runtime();
2418          set_magic_quotes_runtime(0);
2419          if($type=='jpg' || $type=='jpeg')
2420            $info=$this->_parsejpg($file);
2421          elseif($type=='png')
2422            $info=$this->_parsepng($file);
2423          else
2424            {
2425              //Allow for additional formats
2426              $mtd='_parse'.$type;
2427              if(!method_exists($this,$mtd))
2428                $this->Error('Unsupported image type: '.$type);
2429              $info=$this->$mtd($file);
2430            }
2431          set_magic_quotes_runtime($mqr);
2432          $info['i']=count($this->images)+1;
2433          $this->images[$file]=$info;
2434	}
2435      else {
2436        $info=$this->images[$file];
2437      };
2438
2439      //Automatic width and height calculation if needed
2440      if ($w==0 && $h==0) {
2441        //Put image at 72 dpi
2442        $w=$info['w']/$this->k;
2443        $h=$info['h']/$this->k;
2444      }
2445
2446      if ($w==0) {
2447        $w=$h*$info['w']/$info['h'];
2448      };
2449
2450      if ($h==0) {
2451        $h=$w*$info['h']/$info['w'];
2452      };
2453
2454      $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
2455      if ($link) {
2456        $this->Link($x,$y,$w,$h,$link);
2457      };
2458    }
2459
2460    function Ln($h='')
2461    {
2462      //Line feed; default value is last cell height
2463      $this->x=$this->lMargin;
2464      if(is_string($h))
2465        $this->y+=$this->lasth;
2466      else
2467        $this->y+=$h;
2468    }
2469
2470    function GetX()
2471    {
2472      //Get x position
2473      return $this->x;
2474    }
2475
2476    function SetX($x)
2477    {
2478      //Set x position
2479      if($x>=0)
2480        $this->x=$x;
2481      else
2482        $this->x=$this->w+$x;
2483    }
2484
2485    function GetY()
2486    {
2487      //Get y position
2488      return $this->y;
2489    }
2490
2491    function SetY($y)
2492    {
2493      //Set y position and reset x
2494      $this->x=$this->lMargin;
2495      if($y>=0)
2496        $this->y=$y;
2497      else
2498        $this->y=$this->h+$y;
2499    }
2500
2501    function SetXY($x,$y)
2502    {
2503      //Set x and y positions
2504      $this->SetY($y);
2505      $this->SetX($x);
2506    }
2507
2508    function Output($name='',$dest='')
2509    {
2510      //Output PDF to some destination
2511      //Finish document if necessary
2512      if($this->state<3)
2513        $this->Close();
2514      //Normalize parameters
2515      if(is_bool($dest))
2516        $dest=$dest ? 'D' : 'F';
2517      $dest=strtoupper($dest);
2518      if($dest=='')
2519	{
2520          if($name=='')
2521            {
2522              $name='doc.pdf';
2523              $dest='I';
2524            }
2525          else
2526            $dest='F';
2527	}
2528      switch($dest)
2529	{
2530        case 'I':
2531          //Send to standard output
2532          if(ob_get_contents())
2533            $this->Error('Some data has already been output, can\'t send PDF file');
2534          if(php_sapi_name()!='cli')
2535            {
2536              //We send to a browser
2537              header('Content-Type: application/pdf');
2538              if(headers_sent())
2539                $this->Error('Some data has already been output to browser, can\'t send PDF file');
2540              header('Content-Length: '.strlen($this->buffer));
2541              header('Content-disposition: inline; filename="'.$name.'"');
2542            }
2543          echo $this->buffer;
2544          break;
2545        case 'D':
2546          //Download file
2547          if(ob_get_contents())
2548            $this->Error('Some data has already been output, can\'t send PDF file');
2549          if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))
2550            header('Content-Type: application/force-download');
2551          else
2552            header('Content-Type: application/octet-stream');
2553          if(headers_sent())
2554            $this->Error('Some data has already been output to browser, can\'t send PDF file');
2555          header('Content-Length: '.strlen($this->buffer));
2556          header('Content-disposition: attachment; filename="'.$name.'"');
2557          echo $this->buffer;
2558          break;
2559        case 'F':
2560          //Save to local file
2561          $f=fopen($name,'wb');
2562          if(!$f)
2563            $this->Error('Unable to create output file: '.$name);
2564          fwrite($f,$this->buffer,strlen($this->buffer));
2565          fclose($f);
2566          break;
2567        case 'S':
2568          //Return as a string
2569          return $this->buffer;
2570        default:
2571          $this->Error('Incorrect output destination: '.$dest);
2572	}
2573      return '';
2574    }
2575
2576    /*******************************************************************************
2577     *                                                                              *
2578     *                              Protected methods                               *
2579     *                                                                              *
2580     *******************************************************************************/
2581    function _dochecks()
2582    {
2583      //Check for locale-related bug
2584      if(1.1==1)
2585        $this->Error('Don\'t alter the locale before including class file');
2586      //Check for decimal separator
2587      if(sprintf('%.1f',1.0)!='1.0')
2588        setlocale(LC_NUMERIC,'C');
2589    }
2590
2591    function _getfontpath()
2592    {
2593      if(!defined('FPDF_FONTPATH') && is_dir(dirname(__FILE__).'/font'))
2594        define('FPDF_FONTPATH',dirname(__FILE__).'/font/');
2595      return defined('FPDF_FONTPATH') ? FPDF_FONTPATH : '';
2596    }
2597
2598    function _putpages() {
2599      $nb=$this->page;
2600      if (!empty($this->AliasNbPages)) {
2601        //Replace number of pages
2602        for ($n=1; $n<=$nb; $n++) {
2603          $this->pages[$n]=str_replace($this->AliasNbPages,$nb,$this->pages[$n]);
2604        };
2605      }
2606
2607      if ($this->DefOrientation=='P') {
2608        $wPt=$this->fwPt;
2609        $hPt=$this->fhPt;
2610      } else {
2611        $wPt=$this->fhPt;
2612        $hPt=$this->fwPt;
2613      };
2614
2615      $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
2616
2617      $pages_start_obj_number = $this->n+1;
2618
2619      for ($n=1; $n<=$nb; $n++) {
2620        //Page
2621        // $this->_newobj();
2622
2623        $page = $this->_pages[$n-1];
2624        $this->offsets[$page->get_object_id()] = strlen($this->buffer);
2625        $this->_out(sprintf("%u %u obj",$page->object_id, $page->generation_id));
2626
2627        $this->_out('<</Type /Page');
2628        $this->_out('/Parent 1 0 R');
2629        $this->_out("/Annots ".$this->_pages[$n-1]->_annotations($this));
2630
2631        if (isset($this->OrientationChanges[$n])) {
2632          $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt));
2633        };
2634
2635        $this->_out('/Resources 2 0 R');
2636
2637        $this->_out('/Contents '.($this->n+1).' 0 R>>');
2638        $this->_out('endobj');
2639        //Page content
2640        $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
2641        $this->_newobj();
2642        $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
2643        $this->_putstream($p);
2644        $this->_out('endobj');
2645
2646        // Output annotation object for this page
2647        $annotations = $this->_pages[$n-1]->annotations;
2648        $size = count($annotations);
2649
2650        for ($j=0; $j<$size; $j++) {
2651          $annotations[$j]->out($this);
2652        };
2653      }
2654
2655      //Pages root
2656      $this->offsets[1] = strlen($this->buffer);
2657      $this->_out('1 0 obj');
2658      $this->_out('<</Type /Pages');
2659
2660      $this->_out('/Kids '.$this->_reference_array($this->_pages));
2661
2662      $this->_out('/Count '.$nb);
2663      $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt));
2664      $this->_out('>>');
2665      $this->_out('endobj');
2666
2667      return $pages_start_obj_number;
2668    }
2669
2670    function _putfonts() {
2671      $nf=$this->n;
2672      foreach($this->diffs as $diff) {
2673        //Encodings
2674        $this->_newobj();
2675        $this->_out($this->_dictionary(array("Type" => "/Encoding",
2676                                             "BaseEncoding" => "/WinAnsiEncoding",
2677                                             "Differences"  => $this->_array($diff))));
2678        $this->_out('endobj');
2679      }
2680
2681      $mqr=get_magic_quotes_runtime();
2682      set_magic_quotes_runtime(0);
2683      foreach ($this->FontFiles as $file=>$info) {
2684        //Font file embedding
2685        $this->_newobj();
2686        $this->FontFiles[$file]['n'] = $this->n;
2687        $font='';
2688        $f=fopen($this->_getfontpath().$file,'rb',1);
2689        if (!$f) {
2690          $this->Error('Font file not found');
2691        };
2692
2693        while(!feof($f)) { $font.=fread($f,8192); };
2694
2695        fclose($f);
2696        $compressed=(substr($file,-2)=='.z');
2697        if(!$compressed && isset($info['length2'])) {
2698          $header=(ord($font{0})==128);
2699          if($header) {
2700            //Strip first binary header
2701            $font=substr($font,6);
2702          }
2703          if($header && ord($font{$info['length1']})==128) {
2704            //Strip second binary header
2705            $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6);
2706          }
2707        }
2708        $this->_out('<</Length '.strlen($font));
2709
2710        if ($compressed) {
2711          $this->_out('/Filter /FlateDecode');
2712        };
2713
2714        $this->_out('/Length1 '.$info['length1']);
2715        if(isset($info['length2'])) {
2716          $this->_out('/Length2 '.$info['length2'].' /Length3 0');
2717        };
2718        $this->_out('>>');
2719        $this->_putstream($font);
2720        $this->_out('endobj');
2721      }
2722      set_magic_quotes_runtime($mqr);
2723
2724      foreach ($this->fonts as $k=>$font) {
2725        //Font objects
2726        $this->fonts[$k]['n'] = $this->n+1;
2727        $type=$font['type'];
2728        $name=$font['name'];
2729        if ($type=='Type1' || $type=='TrueType') {
2730          //Additional Type1 or TrueType font
2731          $this->_newobj();
2732          $this->_out('<</Type /Font');
2733          $this->_out('/BaseFont /'.$name);
2734          $this->_out('/Subtype /'.$type);
2735          $this->_out('/FirstChar 32 /LastChar 255');
2736          $this->_out('/Widths '.($this->n+1).' 0 R');
2737          $this->_out('/FontDescriptor '.($this->n+2).' 0 R');
2738          if ($font['enc']) {
2739            if(isset($font['diff'])) {
2740              $this->_out('/Encoding '.($nf+$font['diff']).' 0 R');
2741            } else {
2742              $this->_out('/Encoding /WinAnsiEncoding');
2743            };
2744          }
2745          $this->_out('>>');
2746          $this->_out('endobj');
2747
2748          //Widths
2749          $this->_newobj();
2750          $cw=&$font['cw'];
2751          $s='[';
2752          for ($i=32;$i<=255;$i++) {
2753            $s.=$cw[chr($i)].' ';
2754          };
2755          $this->_out($s.']');
2756          $this->_out('endobj');
2757          //Descriptor
2758          $this->_newobj();
2759          $s='<</Type /FontDescriptor /FontName /'.$name;
2760          foreach($font['desc'] as $k=>$v) {
2761            $s.=' /'.$k.' '.$v;
2762          };
2763          $file=$font['file'];
2764          if($file) {
2765            $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
2766          };
2767          $this->_out($s.'>>');
2768          $this->_out('endobj');
2769        } else {
2770          //Allow for additional types
2771          $mtd='_put'.strtolower($type);
2772          if(!method_exists($this,$mtd))
2773            $this->Error('Unsupported font type: '.$type);
2774          $this->$mtd($font);
2775        }
2776      }
2777    }
2778
2779    function _putimages() {
2780      $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
2781      reset($this->images);
2782      while (list($file,$info) = each($this->images)) {
2783        $this->_newobj();
2784        $this->images[$file]['n']=$this->n;
2785        $this->_out('<</Type /XObject');
2786        $this->_out('/Subtype /Image');
2787        $this->_out('/Width '.$info['w']);
2788        $this->_out('/Height '.$info['h']);
2789        if ($info['cs']=='Indexed') {
2790          $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
2791        } else {
2792          $this->_out('/ColorSpace /'.$info['cs']);
2793          if($info['cs']=='DeviceCMYK') {
2794            $this->_out('/Decode [1 0 1 0 1 0 1 0]');
2795          };
2796        }
2797        $this->_out('/BitsPerComponent '.$info['bpc']);
2798        if (isset($info['f'])) {
2799          $this->_out('/Filter /'.$info['f']);
2800        };
2801
2802        if(isset($info['parms'])) {
2803          $this->_out($info['parms']);
2804        };
2805
2806        if(isset($info['trns']) && is_array($info['trns'])) {
2807          $trns='';
2808          for ($i=0;$i<count($info['trns']);$i++) {
2809            $trns.=$info['trns'][$i].' '.$info['trns'][$i].' ';
2810          };
2811          $this->_out('/Mask ['.$trns.']');
2812        };
2813
2814        $this->_out('/Length '.strlen($info['data']).'>>');
2815        $this->_putstream($info['data']);
2816        unset($this->images[$file]['data']);
2817        $this->_out('endobj');
2818
2819        // Palette
2820        if ($info['cs']=='Indexed') {
2821          $this->_newobj();
2822          $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal'];
2823          $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
2824          $this->_putstream($pal);
2825          $this->_out('endobj');
2826        };
2827      }
2828    }
2829
2830    function _putxobjectdict() {
2831      foreach($this->images as $image)
2832        $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
2833    }
2834
2835    function _putresourcedict()
2836    {
2837      $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
2838      $this->_out('/Font <<');
2839      foreach($this->fonts as $font)
2840        $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
2841      $this->_out('>>');
2842      $this->_out('/XObject <<');
2843      $this->_putxobjectdict();
2844      $this->_out('>>');
2845    }
2846
2847    function _putresources()
2848    {
2849      $this->_putfonts();
2850      $this->_putimages();
2851      //Resource dictionary
2852      $this->offsets[2]=strlen($this->buffer);
2853      $this->_out('2 0 obj');
2854      $this->_out('<<');
2855      $this->_putresourcedict();
2856      $this->_out('>>');
2857      $this->_out('endobj');
2858    }
2859
2860    function _putinfo()
2861    {
2862      $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION));
2863      if(!empty($this->title))
2864        $this->_out('/Title '.$this->_textstring($this->title));
2865      if(!empty($this->subject))
2866        $this->_out('/Subject '.$this->_textstring($this->subject));
2867      if(!empty($this->author))
2868        $this->_out('/Author '.$this->_textstring($this->author));
2869      if(!empty($this->keywords))
2870        $this->_out('/Keywords '.$this->_textstring($this->keywords));
2871      if(!empty($this->creator))
2872        $this->_out('/Creator '.$this->_textstring($this->creator));
2873      $this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis')));
2874    }
2875
2876    // Generate the document catalog entry of PDF file
2877    function _putcatalog($pages_start_obj_number) {
2878      $this->_out('/Type /Catalog');
2879
2880      $this->_out('/Pages 1 0 R');
2881      if ($this->ZoomMode=='fullpage') {
2882        $this->_out("/OpenAction [$pages_start_obj_number 0 R /Fit]");
2883      } elseif ($this->ZoomMode=='fullwidth') {
2884        $this->_out("/OpenAction [$pages_start_obj_number 0 R /FitH null]");
2885      } elseif ($this->ZoomMode=='real') {
2886        $this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null 1]");
2887      } elseif (!is_string($this->ZoomMode)) {
2888        $this->_out("/OpenAction [$pages_start_obj_number 0 R /XYZ null null ".($this->ZoomMode/100).']');
2889      };
2890
2891      if ($this->LayoutMode=='single') {
2892        $this->_out('/PageLayout /SinglePage');
2893      } elseif ($this->LayoutMode=='continuous') {
2894        $this->_out('/PageLayout /OneColumn');
2895      } elseif ($this->LayoutMode=='two') {
2896        $this->_out('/PageLayout /TwoColumnLeft');
2897      };
2898
2899      if (count($this->_forms) > 0) {
2900        $this->_out('/AcroForm <<');
2901        $this->_out('/Fields '.$this->_reference_array($this->_forms));
2902        $this->_out('/DR 2 0 R');
2903        $this->_out('/NeedAppearances true');
2904        $this->_out('>>');
2905      };
2906    }
2907
2908    function _putheader() {
2909      $this->_out('%PDF-'.$this->PDFVersion);
2910    }
2911
2912    function _puttrailer()
2913    {
2914      $this->_out('/Size '.($this->n+1));
2915      $this->_out('/Root '.$this->n.' 0 R');
2916      $this->_out('/Info '.($this->n-1).' 0 R');
2917    }
2918
2919    function _enddoc() {
2920      $this->_putheader();
2921      $pages_start_obj_number = $this->_putpages();
2922
2923      $this->_putresources();
2924      //Info
2925      $this->_newobj();
2926      $this->_out('<<');
2927      $this->_putinfo();
2928      $this->_out('>>');
2929      $this->_out('endobj');
2930
2931      // Form fields
2932      for ($i=0; $i<count($this->_forms); $i++) {
2933        $form =& $this->_forms[$i];
2934
2935        $form->out($this);
2936
2937//         // @todo: remove this dirty hack
2938//         if (isset($form->appearance)) {
2939//           $this->offsets[$form->appearance->get_object_id()] = strlen($this->buffer);
2940//           $this->_out($this->_indirect_object($form->appearance));
2941//         };
2942
2943//         if (isset($form->_radios)) {
2944
2945//            for ($i=0; $i<count($form->_radios); $i++) {
2946
2947// //             $radio = $form->_radios[$i];
2948// //             $this->offsets[$radio->get_object_id()] = strlen($this->buffer);
2949// //             $this->_out($this->_indirect_object($radio));
2950//            };
2951//         }
2952
2953        // Not required, as forms fields are annotations which are output at the end of the page
2954//         $this->offsets[$form->get_object_id()] = strlen($this->buffer);
2955//         $this->_out($this->_indirect_object($form));
2956      };
2957
2958      //Catalog
2959      $this->_newobj();
2960      $this->_out('<<');
2961      $this->_putcatalog($pages_start_obj_number);
2962      $this->_out('>>');
2963      $this->_out('endobj');
2964
2965      //Cross-ref
2966      $o=strlen($this->buffer);
2967      $this->_out('xref');
2968      $this->_out('0 '.($this->n+1));
2969      $this->_out('0000000000 65535 f ');
2970
2971      for ($i=1; $i<=$this->n; $i++) {
2972        $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
2973      };
2974
2975      //Trailer
2976      $this->_out('trailer');
2977      $this->_out('<<');
2978      $this->_puttrailer();
2979      $this->_out('>>');
2980      $this->_out('startxref');
2981      $this->_out($o);
2982      $this->_out('%%EOF');
2983      $this->state=3;
2984    }
2985
2986    function _beginpage($orientation) {
2987      $this->page++;
2988      $this->pages[$this->page]='';
2989      $this->state=2;
2990      $this->x=$this->lMargin;
2991      $this->y=$this->tMargin;
2992      $this->FontFamily='';
2993
2994      //Page orientation
2995      if(!$orientation) {
2996        $orientation=$this->DefOrientation;
2997      } else {
2998        $orientation=strtoupper($orientation{0});
2999        if($orientation!=$this->DefOrientation) {
3000          $this->OrientationChanges[$this->page]=true;
3001        };
3002      }
3003
3004      if($orientation!=$this->CurOrientation) {
3005        //Change orientation
3006        if($orientation=='P') {
3007          $this->wPt=$this->fwPt;
3008          $this->hPt=$this->fhPt;
3009          $this->w=$this->fw;
3010          $this->h=$this->fh;
3011        }
3012        else
3013          {
3014            $this->wPt=$this->fhPt;
3015            $this->hPt=$this->fwPt;
3016            $this->w=$this->fh;
3017            $this->h=$this->fw;
3018          }
3019        $this->PageBreakTrigger=$this->h-$this->bMargin;
3020        $this->CurOrientation=$orientation;
3021      }
3022    }
3023
3024    function _endpage() {
3025      //End of page contents
3026      $this->state=1;
3027    }
3028
3029    // Start a new indirect object
3030    function _newobj() {
3031      $num = $this->_generate_new_object_number();
3032      $this->offsets[$num]=strlen($this->buffer);
3033      $this->_out($num.' 0 obj');
3034    }
3035
3036    function _parsejpg($file)
3037    {
3038      //Extract info from a JPEG file
3039      $a=GetImageSize($file);
3040
3041      if (!$a) {
3042        $this->Error('Missing or incorrect image file: '.$file);
3043      };
3044
3045      if ($a[2]!=2) {
3046        $this->Error('Not a JPEG file: '.$file);
3047      };
3048
3049      if (!isset($a['channels']) || $a['channels']==3) {
3050        $colspace='DeviceRGB';
3051      } elseif ($a['channels']==4) {
3052        $colspace='DeviceCMYK';
3053      } else {
3054        $colspace='DeviceGray';
3055      };
3056
3057      $bpc=isset($a['bits']) ? $a['bits'] : 8;
3058
3059      //Read whole file
3060      $f    = fopen($file,'rb');
3061      $data = '';
3062      while (!feof($f)) {
3063        $data .= fread($f,4096);
3064      };
3065      fclose($f);
3066      return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data);
3067    }
3068
3069    function _parsepng($file) {
3070      //Extract info from a PNG file
3071      $f=fopen($file,'rb');
3072      if (!$f) {
3073        $this->Error('Can\'t open image file: '.$file);
3074      };
3075
3076      //Check signature
3077      if (fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3078        $this->Error('Not a PNG file: '.$file);
3079      };
3080
3081      //Read header chunk
3082      fread($f,4);
3083      if (fread($f,4)!='IHDR') {
3084        $this->Error('Incorrect PNG file: '.$file);
3085      };
3086
3087      $w=$this->_freadint($f);
3088      $h=$this->_freadint($f);
3089      $bpc=ord(fread($f,1));
3090
3091      if ($bpc>8) {
3092        $this->Error('16-bit depth not supported: '.$file);
3093      };
3094
3095      $ct=ord(fread($f,1));
3096      if ($ct==0) {
3097        $colspace='DeviceGray';
3098      } elseif($ct==2) {
3099        $colspace='DeviceRGB';
3100      } elseif($ct==3) {
3101        $colspace='Indexed';
3102      } else {
3103        $this->Error('Alpha channel not supported: '.$file);
3104      };
3105
3106      if (ord(fread($f,1))!=0) {
3107        $this->Error('Unknown compression method: '.$file);
3108      };
3109
3110      if (ord(fread($f,1))!=0) {
3111        $this->Error('Unknown filter method: '.$file);
3112      };
3113
3114      if (ord(fread($f,1))!=0) {
3115        $this->Error('Interlacing not supported: '.$file);
3116      };
3117
3118      fread($f,4);
3119      $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3120
3121      //Scan chunks looking for palette, transparency and image data
3122      $pal='';
3123      $trns='';
3124      $data='';
3125      do
3126	{
3127          $n=$this->_freadint($f);
3128          $type=fread($f,4);
3129          if($type=='PLTE')
3130            {
3131              //Read palette
3132              $pal=fread($f,$n);
3133              fread($f,4);
3134            }
3135          elseif($type=='tRNS')
3136            {
3137              //Read transparency info
3138              $t=fread($f,$n);
3139              if($ct==0)
3140                $trns=array(ord(substr($t,1,1)));
3141              elseif($ct==2)
3142                $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));
3143              else
3144                {
3145                  $pos=strpos($t,chr(0));
3146                  if($pos!==false)
3147                    $trns=array($pos);
3148                }
3149              fread($f,4);
3150            }
3151          elseif($type=='IDAT')
3152            {
3153              //Read image data block
3154              $data.=fread($f,$n);
3155              fread($f,4);
3156            }
3157          elseif($type=='IEND')
3158            break;
3159          else
3160            fread($f,$n+4);
3161	}
3162      while($n);
3163      if($colspace=='Indexed' && empty($pal))
3164        $this->Error('Missing palette in '.$file);
3165      fclose($f);
3166      return array('w'=>$w,'h'=>$h,'cs'=>$colspace,'bpc'=>$bpc,'f'=>'FlateDecode','parms'=>$parms,'pal'=>$pal,'trns'=>$trns,'data'=>$data);
3167    }
3168
3169    function _freadint($f)
3170    {
3171      //Read a 4-byte integer from file
3172      $a=unpack('Ni',fread($f,4));
3173      return $a['i'];
3174    }
3175
3176    function _textstring($s)
3177    {
3178      //Format a text string
3179      return '('.$this->_escape($s).')';
3180    }
3181
3182    function _escape($s) {
3183      //Add \ before \, ( and )
3184      return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s)));
3185    }
3186
3187    function _putstream($s) {
3188      $this->_out('stream');
3189      $this->_out($s);
3190      $this->_out('endstream');
3191    }
3192
3193    function _out($s) {
3194      //Add a line to the document
3195      if($this->state==2)
3196        $this->pages[$this->page].=$s."\n";
3197      else
3198        $this->buffer.=$s."\n";
3199    }
3200    //End of class
3201  }
3202
3203  //Handle special IE contype request
3204  if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype')
3205    {
3206      header('Content-Type: application/pdf');
3207      exit;
3208    }
3209
3210
3211}
3212?>
3213