Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
 
17
if (!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
?>