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