Completed
Push — master ( 06c905...d81d62 )
by Hannes
01:12
created

TCPDI::importAnnotations()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.0555
c 0
b 0
f 0
cc 9
nc 5
nop 1
1
<?php
2
//
3
//  TCPDI - Version 1.1
4
//  Based on FPDI - Version 1.4.4
5
//
6
//    Copyright 2004-2013 Setasign - Jan Slabon
7
//
8
//  Licensed under the Apache License, Version 2.0 (the "License");
9
//  you may not use this file except in compliance with the License.
10
//  You may obtain a copy of the License at
11
//
12
//      http://www.apache.org/licenses/LICENSE-2.0
13
//
14
//  Unless required by applicable law or agreed to in writing, software
15
//  distributed under the License is distributed on an "AS IS" BASIS,
16
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
//  See the License for the specific language governing permissions and
18
//  limitations under the License.
19
//
20
21
// Dummy shim to allow unmodified use of fpdf_tpl
22
class FPDF extends TCPDF {}
23
24
require_once('fpdf_tpl.php');
25
26
require_once('tcpdi_parser.php');
27
28
29
class TCPDI extends FPDF_TPL {
30
    /**
31
     * Actual filename
32
     * @var string
33
     */
34
    public $current_filename;
35
36
    /**
37
     * Parser-Objects
38
     * @var array
39
     */
40
    public $parsers = array();
41
42
    /**
43
     * Current parser
44
     * @var object
45
     */
46
    public $current_parser;
47
48
    /**
49
     * object stack
50
     * @var array
51
     */
52
    protected $_obj_stack = array();
53
54
    /**
55
     * done object stack
56
     * @var array
57
     */
58
    protected $_don_obj_stack = array();
59
60
    /**
61
     * Current Object Id.
62
     * @var integer
63
     */
64
    protected $_current_obj_id;
65
66
    /**
67
     * The name of the last imported page box
68
     * @var string
69
     */
70
    public $lastUsedPageBox;
71
72
    /**
73
     * Cache for imported pages/template ids
74
     * @var array
75
     */
76
    protected $_importedPages = array();
77
78
    /**
79
     * Cache for imported page annotations
80
     * @var array
81
     */
82
    protected $_importedAnnots  = array();
83
84
    /**
85
     * Number of TOC pages, used for annotation offset
86
     * @var integer
87
     */
88
    protected $_numTOCpages  = 0;
89
90
    /**
91
     * First TOC page, used for annotation offset
92
     * @var integer
93
     */
94
    protected $_TOCpagenum  = 0;
95
96
    /**
97
     * Set a source-file
98
     *
99
     * @param string $filename a valid filename
100
     * @return int number of available pages
101
     */
102
    public function setSourceFile($filename) {
103
        $this->current_filename = $filename;
104
105
        if (!isset($this->parsers[$filename]))
106
            $this->parsers[$filename] = $this->_getPdfParser($filename);
107
        $this->current_parser =& $this->parsers[$filename];
108
        $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
109
110
        return $this->parsers[$filename]->getPageCount();
111
    }
112
113
    /**
114
     * Set a source-file PDF data
115
     *
116
     * @param string $pdfdata The PDF file content
117
     * @return int number of available pages
118
     */
119
    public function setSourceData($pdfdata) {
120
        $filename = uniqid('tcpdi-');
121
        $this->current_filename = $filename;
122
123
        if (!isset($this->parsers[$filename]))
124
            $this->parsers[$filename] = new tcpdi_parser($pdfdata, $filename);
125
        $this->current_parser =& $this->parsers[$filename];
126
        $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
127
128
        return $this->parsers[$filename]->getPageCount();
129
    }
130
131
    /**
132
     * Returns a PDF parser object
133
     *
134
     * @param string $filename
135
     * @return fpdi_pdf_parser
136
     */
137
    protected function _getPdfParser($filename) {
138
        $data = file_get_contents($filename);
139
        return new tcpdi_parser($data, $filename);
140
    }
141
142
    /**
143
     * Get the current PDF version
144
     *
145
     * @return string
146
     */
147
    public function getPDFVersion() {
148
        return $this->PDFVersion;
149
    }
150
151
    /**
152
     * Set the PDF version
153
     *
154
     * @return string
155
     */
156
    public function setPDFVersion($version = '1.3') {
157
        $this->PDFVersion = $version;
158
    }
159
160
    /**
161
     * Import a page
162
     *
163
     * @param int $pageno pagenumber
164
     * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
165
     */
166
    public function importPage($pageno, $boxName = '/CropBox') {
167
        if ($this->_intpl) {
168
            return $this->error('Please import the desired pages before creating a new template.');
169
        }
170
171
        $fn = $this->current_filename;
172
173
        // check if page already imported
174
        $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
175
        if (isset($this->_importedPages[$pageKey]))
176
            return $this->_importedPages[$pageKey];
177
178
        $parser =& $this->parsers[$fn];
179
        $parser->setPageno($pageno);
180
181
        if (!in_array($boxName, $parser->availableBoxes))
182
            return $this->Error(sprintf('Unknown box: %s', $boxName));
183
184
        $pageboxes = $parser->getPageBoxes($pageno, $this->k);
185
186
        /**
187
         * MediaBox
188
         * CropBox: Default -> MediaBox
189
         * BleedBox: Default -> CropBox
190
         * TrimBox: Default -> CropBox
191
         * ArtBox: Default -> CropBox
192
         */
193
        if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
194
            $boxName = '/CropBox';
195
        if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
196
            $boxName = '/MediaBox';
197
198
        if (!isset($pageboxes[$boxName]))
199
            return false;
200
201
        $this->lastUsedPageBox = $boxName;
202
203
        $box = $pageboxes[$boxName];
204
205
        $this->tpl++;
206
        $this->tpls[$this->tpl] = array();
207
        $tpl =& $this->tpls[$this->tpl];
208
        $tpl['parser'] =& $parser;
209
        $tpl['resources'] = $parser->getPageResources();
210
        $tpl['buffer'] = $parser->getContent();
211
        $tpl['box'] = $box;
212
213
        // To build an array that can be used by PDF_TPL::useTemplate()
214
        $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
215
216
        // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
217
        $tpl['x'] = 0;
218
        $tpl['y'] = 0;
219
220
        // handle rotated pages
221
        $rotation = $parser->getPageRotation($pageno);
222
        $tpl['_rotationAngle'] = 0;
223
        if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
224
            $steps = $angle / 90;
225
226
            $_w = $tpl['w'];
227
            $_h = $tpl['h'];
228
            $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
229
            $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
230
231
            if ($angle < 0)
232
                $angle += 360;
233
234
            $tpl['_rotationAngle'] = $angle * -1;
235
        }
236
237
        $this->_importedPages[$pageKey] = $this->tpl;
238
239
        return $this->tpl;
240
    }
241
242
    public function setPageFormatFromTemplatePage($pageno, $orientation) {
243
        $fn = $this->current_filename;
244
        $parser =& $this->parsers[$fn];
245
        $parser->setPageno($pageno);
246
        $boxes = $parser->getPageBoxes($pageno, $this->k);
247
        foreach ($boxes as $name => $box) {
248
            if ($name[0] == '/') {
249
                $boxes[substr($name, 1)] = $box;
250
                unset($boxes[$name]);
251
            }
252
        }
253
        $this->setPageFormat($boxes, $orientation);
254
    }
255
256
    /* Wrapper for AddPage() which tracks TOC pages to offset annotations later */
257
    public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
258
        if ($this->inxobj) {
259
            // we are inside an XObject template
260
            return;
261
        }
262
        parent::AddPage($orientation, $format, $keepmargins, $tocpage);
263
        if ($this->tocpage) {
264
            $this->_numTOCpages++;
265
        }
266
    }
267
268
    /* Wrapper for AddTOC() which tracks TOC position to offset annotations later */
269
    public function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
270
        if (!TCPDF_STATIC::empty_string($page)) {
271
            $this->_TOCpagenum = $page;
272
        } else {
273
            $this->_TOCpagenum = $this->page;
274
        }
275
276
        parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color);
277
    }
278
279
    public function importAnnotations($pageno) {
280
        $fn = $this->current_filename;
281
        $parser =& $this->parsers[$fn];
282
        $parser->setPageno($pageno);
283
        $annots = $parser->getPageAnnotations();
284
285
        if (is_array($annots) && $annots[0] == PDF_TYPE_OBJECT // We got an object
286
                && is_array($annots[1]) && $annots[1][0] == PDF_TYPE_ARRAY // It's an array
287
                && is_array($annots[1][1]) && count($annots[1][1] > 1) // It's not empty - there are annotations for this page
288
        ) {
289
            if (!isset($this->_obj_stack[$fn])) {
290
                $this->_obj_stack[$fn] = array();
291
            }
292
293
            $this->_importedAnnots[$this->page] = array();
294
            foreach ($annots[1][1] as $annot) {
295
                $this->importAnnotation($annot);
296
            }
297
        }
298
    }
299
300
    public function importAnnotation($annotation) {
301
        $fn = $this->current_filename;
302
        $old_id = $annotation[1];
303
        $value = array(PDF_TYPE_OBJREF, $old_id, 0);
304
        if (!isset($this->_don_obj_stack[$fn][$old_id])) {
305
            $this->_newobj(false, true);
306
            $this->_obj_stack[$fn][$old_id] = array($this->n, $value);
307
            $this->_don_obj_stack[$fn][$old_id] = array($this->n, $value);
308
        }
309
        $objid = $this->_don_obj_stack[$fn][$old_id][0];
310
        $this->_importedAnnots[$this->page][] = $objid;
311
    }
312
313
    /**
314
     * Get references to page annotations.
315
     * @param $n (int) page number
316
     * @return string
317
     * @protected
318
     * @author Nicola Asuni
319
     * @since 5.0.010 (2010-05-17)
320
     */
321
    protected function _getannotsrefs($n) {
322
        if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) {
323
            // Offset page number to account for TOC being inserted before page containing annotations.
324
            $n -= $this->_numTOCpages;
325
        }
326
        if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
327
            return '';
328
        }
329
        $out = ' /Annots [';
330
        if (isset($this->_importedAnnots[$n])) {
331
            foreach ($this->_importedAnnots[$n] as $key => $val) {
332
                $out .= ' '.$val.' 0 R';
333
            }
334
        }
335
        if (isset($this->PageAnnots[$n])) {
336
            foreach ($this->PageAnnots[$n] as $key => $val) {
337
                if (!in_array($val['n'], $this->radio_groups)) {
338
                    $out .= ' '.$val['n'].' 0 R';
339
                }
340
            }
341
            // add radiobutton groups
342
            if (isset($this->radiobutton_groups[$n])) {
343
                foreach ($this->radiobutton_groups[$n] as $key => $data) {
344
                    if (isset($data['n'])) {
345
                        $out .= ' '.$data['n'].' 0 R';
346
                    }
347
                }
348
            }
349
        }
350
        if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
351
            // set reference for signature object
352
            $out .= ' '.$this->sig_obj_id.' 0 R';
353
        }
354
        if (!empty($this->empty_signature_appearance)) {
355
            foreach ($this->empty_signature_appearance as $esa) {
356
                if ($esa['page'] == $n) {
357
                    // set reference for empty signature objects
358
                    $out .= ' '.$esa['objid'].' 0 R';
359
                }
360
            }
361
        }
362
        $out .= ' ]';
363
        return $out;
364
    }
365
366
    /**
367
     * Returns the last used page box
368
     *
369
     * @return string
370
     */
371
    public function getLastUsedPageBox() {
372
        return $this->lastUsedPageBox;
373
    }
374
375
376
    public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
377
        if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
378
            $size = $this->getTemplateSize($tplidx, $_w, $_h);
379
            $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
380
            $size = array($size['w'], $size['h']);
381
382
            $this->setPageFormat($size, $orientation);
383
        }
384
385
        $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
386
        $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
387
        $this->_out('Q');
388
389
        return $s;
390
    }
391
392
    /**
393
     * Private method, that rebuilds all needed objects of source files
394
     */
395
    public function _putimportedobjects() {
396
        if (is_array($this->parsers) && count($this->parsers) > 0) {
397
            foreach($this->parsers AS $filename => $p) {
398
                $this->current_parser =& $this->parsers[$filename];
399
                if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
400
                    while(($n = key($this->_obj_stack[$filename])) !== null) {
401
                        $nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]);
402
403
                        $this->_newobj($this->_obj_stack[$filename][$n][0]);
404
405
                        if ($nObj[0] == PDF_TYPE_STREAM) {
406
                            $this->pdf_write_value($nObj);
407
                        } else {
408
                            $this->pdf_write_value($nObj[1]);
409
                        }
410
411
                        $this->_out('endobj');
412
                        $this->_obj_stack[$filename][$n] = null; // free memory
413
                        unset($this->_obj_stack[$filename][$n]);
414
                        reset($this->_obj_stack[$filename]);
415
                    }
416
                }
417
418
                // We're done with this parser.  Clean it up to free a bit of RAM.
419
                $this->current_parser->cleanUp();
420
                unset($this->parsers[$filename]);
421
            }
422
        }
423
    }
424
425
426
    /**
427
     * Private Method that writes the form xobjects
428
     */
429
    public function _putformxobjects() {
430
        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
431
        reset($this->tpls);
432
        foreach($this->tpls AS $tplidx => $tpl) {
433
            $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
434
            $this->_newobj();
435
            $cN = $this->n; // TCPDF/Protection: rem current "n"
436
437
            $this->tpls[$tplidx]['n'] = $this->n;
438
            $this->_out('<<' . $filter . '/Type /XObject');
439
            $this->_out('/Subtype /Form');
440
            $this->_out('/FormType 1');
441
442
            $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
443
                (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
444
                (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
445
                (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
446
                (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
447
            ));
448
449
            $c = 1;
450
            $s = 0;
451
            $tx = 0;
452
            $ty = 0;
453
454
            if (isset($tpl['box'])) {
455
                $tx = -$tpl['box']['llx'];
456
                $ty = -$tpl['box']['lly'];
457
458
                if ($tpl['_rotationAngle'] <> 0) {
459
                    $angle = $tpl['_rotationAngle'] * M_PI/180;
460
                    $c=cos($angle);
461
                    $s=sin($angle);
462
463
                    switch($tpl['_rotationAngle']) {
464
                        case -90:
465
                            $tx = -$tpl['box']['lly'];
466
                            $ty = $tpl['box']['urx'];
467
                            break;
468
                        case -180:
469
                            $tx = $tpl['box']['urx'];
470
                            $ty = $tpl['box']['ury'];
471
                            break;
472
                        case -270:
473
                            $tx = $tpl['box']['ury'];
474
                            $ty = -$tpl['box']['llx'];
475
                            break;
476
                    }
477
                }
478
            } elseif ($tpl['x'] != 0 || $tpl['y'] != 0) {
479
                $tx = -$tpl['x'] * 2;
480
                $ty = $tpl['y'] * 2;
481
            }
482
483
            $tx *= $this->k;
484
            $ty *= $this->k;
485
486
            if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
487
                $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
488
                    $c, $s, -$s, $c, $tx, $ty
489
                ));
490
            }
491
492
            $this->_out('/Resources ');
493
494
            if (isset($tpl['resources'])) {
495
                $this->current_parser =& $tpl['parser'];
496
                $this->pdf_write_value($tpl['resources']); // "n" will be changed
497
            } else {
498
                $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
499
                if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
500
                    $this->_out('/Font <<');
501
                    foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
502
                        $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
503
                    $this->_out('>>');
504
                }
505
                if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
506
                    isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
507
                {
508
                    $this->_out('/XObject <<');
509
                    if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
510
                        foreach($this->_res['tpl'][$tplidx]['images'] as $image)
511
                            $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
512
                    }
513
                    if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
514
                        foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
515
                            $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
516
                    }
517
                    $this->_out('>>');
518
                }
519
                $this->_out('>>');
520
            }
521
522
            $this->_out('/Group <</Type/Group/S/Transparency>>');
523
524
            $nN = $this->n; // TCPDF: rem new "n"
525
            $this->n = $cN; // TCPDF: reset to current "n"
526
527
            $p = $this->_getrawstream($p);
528
            $this->_out('/Length ' . strlen($p) . ' >>');
529
            $this->_out("stream\n" . $p . "\nendstream");
530
531
            $this->_out('endobj');
532
            $this->n = $nN; // TCPDF: reset to new "n"
533
        }
534
535
        $this->_putimportedobjects();
536
    }
537
538
    /**
539
     * Rewritten to handle existing own defined objects
540
     */
541
    protected function _newobj($obj_id = false, $onlynewobj = false) {
542
        if (!$obj_id) {
543
            $obj_id = ++$this->n;
544
        }
545
546
        //Begin a new object
547
        if (!$onlynewobj) {
548
            $this->offsets[$obj_id] = $this->bufferlen;
549
            $this->_out($obj_id . ' 0 obj');
550
            $this->_current_obj_id = $obj_id; // for later use with encryption
551
        }
552
553
        return $obj_id;
554
    }
555
556
    /**
557
     * Writes a value
558
     * Needed to rebuild the source document
559
     *
560
     * @param mixed $value A PDF-Value. Structure of values see cases in this method
561
     */
562
    public function pdf_write_value(&$value)
563
    {
564
        switch ($value[0]) {
565
            case PDF_TYPE_STRING:
566
                if ($this->encrypted) {
567
                    $value[1] = $this->_unescape($value[1]);
568
                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
569
                    $value[1] = TCPDF_STATIC::_escape($value[1]);
570
                }
571
                break;
572
573
            case PDF_TYPE_STREAM:
574
                if ($this->encrypted) {
575
                    $value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
576
                    $value[1][1]['/Length'] = array(
577
                        PDF_TYPE_NUMERIC,
578
                        strlen($value[2][1])
579
                    );
580
                }
581
                break;
582
583
            case PDF_TYPE_HEX:
584
                if ($this->encrypted) {
585
                    $value[1] = $this->hex2str($value[1]);
586
                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
587
588
                    // remake hexstring of encrypted string
589
                    $value[1] = $this->str2hex($value[1]);
590
                }
591
                break;
592
        }
593
594
        switch ($value[0]) {
595
596
            case PDF_TYPE_TOKEN:
597
                $this->_straightOut('/'.$value[1] . ' ');
598
                break;
599
            case PDF_TYPE_NUMERIC:
600
            case PDF_TYPE_REAL:
601
                if (is_float($value[1]) && $value[1] != 0) {
602
                    $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
603
                } else {
604
                    $this->_straightOut($value[1] . ' ');
605
                }
606
                break;
607
608
            case PDF_TYPE_ARRAY:
609
610
                // An array. Output the proper
611
                // structure and move on.
612
613
                $this->_straightOut('[');
614
                for ($i = 0; $i < count($value[1]); $i++) {
615
                    $this->pdf_write_value($value[1][$i]);
616
                }
617
618
                $this->_out(']');
619
                break;
620
621
            case PDF_TYPE_DICTIONARY:
622
623
                // A dictionary.
624
                $this->_straightOut('<<');
625
626
                foreach ($value[1] as $k => $v) {
627
                    $this->_straightOut($k . ' ');
628
                    $this->pdf_write_value($v);
629
                }
630
631
                $this->_straightOut('>>');
632
                break;
633
634
            case PDF_TYPE_OBJREF:
635
636
                // An indirect object reference
637
                // Fill the object stack if needed
638
                $cpfn =& $this->current_parser->uniqueid;
639
640
                if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
641
                    $this->_newobj(false, true);
642
                    $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
643
                    $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
644
                }
645
                $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
646
647
                $this->_out($objid . ' 0 R');
648
                break;
649
650
            case PDF_TYPE_STRING:
651
652
                // A string.
653
                $this->_straightOut('(' . $value[1] . ')');
654
655
                break;
656
657
            case PDF_TYPE_STREAM:
658
659
                // A stream. First, output the
660
                // stream dictionary, then the
661
                // stream data itself.
662
                $this->pdf_write_value($value[1]);
663
                $this->_out('stream');
664
                $this->_out($value[2][1]);
665
                $this->_out('endstream');
666
                break;
667
668
            case PDF_TYPE_HEX:
669
                $this->_straightOut('<' . $value[1] . '>');
670
                break;
671
672
            case PDF_TYPE_BOOLEAN:
673
                $this->_straightOut($value[1] ? 'true ' : 'false ');
674
                break;
675
676
            case PDF_TYPE_NULL:
677
                // The null object.
678
679
                $this->_straightOut('null ');
680
                break;
681
        }
682
    }
683
684
    /**
685
     * Modified so not each call will add a newline to the output.
686
     */
687
    protected function _straightOut($s) {
688
        if ($this->state == 2) {
689
            if ($this->inxobj) {
690
                // we are inside an XObject template
691
                $this->xobjects[$this->xobjid]['outdata'] .= $s;
692
            } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
693
                // puts data before page footer
694
                $pagebuff = $this->getPageBuffer($this->page);
695
                $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
696
                $footer = substr($pagebuff, -$this->footerlen[$this->page]);
697
                $this->setPageBuffer($this->page, $page.$s.$footer);
698
                // update footer position
699
                $this->footerpos[$this->page] += strlen($s);
700
            } else {
701
                // set page data
702
                $this->setPageBuffer($this->page, $s, true);
703
            }
704
        } elseif ($this->state > 0) {
705
            // set general data
706
            $this->setBuffer($s);
707
        }
708
    }
709
710
    /**
711
     * rewritten to close opened parsers
712
     *
713
     */
714
    protected function _enddoc() {
715
        parent::_enddoc();
716
        $this->_closeParsers();
717
    }
718
719
    /**
720
     * close all files opened by parsers
721
     */
722
    protected function _closeParsers() {
723
        if ($this->state > 2 && count($this->parsers) > 0) {
724
            $this->cleanUp();
725
            return true;
726
        }
727
        return false;
728
    }
729
730
    /**
731
     * Removes cylced references and closes the file handles of the parser objects
732
     */
733
    public function cleanUp() {
734
        foreach ($this->parsers as $k => $_){
735
            $this->parsers[$k]->cleanUp();
736
            $this->parsers[$k] = null;
737
            unset($this->parsers[$k]);
738
        }
739
    }
740
741
    // Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it
742
    protected function _putstream($s, $n=0) {
743
        $this->_out($this->_getstream($s, $n));
744
    }
745
746
    protected function _getxobjectdict() {
747
        $out = parent::_getxobjectdict();
748
        if (count($this->tpls)) {
749
            foreach($this->tpls as $tplidx => $tpl) {
750
                $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
751
            }
752
        }
753
754
        return $out;
755
    }
756
757
    /**
758
     * Unescapes a PDF string
759
     *
760
     * @param string $s
761
     * @return string
762
     */
763
    protected function _unescape($s) {
764
        $out = '';
765
        for ($count = 0, $n = strlen($s); $count < $n; $count++) {
766
            if ($s[$count] != '\\' || $count == $n-1) {
767
                $out .= $s[$count];
768
            } else {
769
                switch ($s[++$count]) {
770
                    case ')':
771
                    case '(':
772
                    case '\\':
773
                        $out .= $s[$count];
774
                        break;
775
                    case 'f':
776
                        $out .= chr(0x0C);
777
                        break;
778
                    case 'b':
779
                        $out .= chr(0x08);
780
                        break;
781
                    case 't':
782
                        $out .= chr(0x09);
783
                        break;
784
                    case 'r':
785
                        $out .= chr(0x0D);
786
                        break;
787
                    case 'n':
788
                        $out .= chr(0x0A);
789
                        break;
790
                    case "\r":
791
                        if ($count != $n-1 && $s[$count+1] == "\n")
792
                            $count++;
793
                        break;
794
                    case "\n":
795
                        break;
796
                    default:
797
                        // Octal-Values
798
                        if (ord($s[$count]) >= ord('0') &&
799
                            ord($s[$count]) <= ord('9')) {
800
                            $oct = ''. $s[$count];
801
802
                            if (ord($s[$count+1]) >= ord('0') &&
803
                                ord($s[$count+1]) <= ord('9')) {
804
                                $oct .= $s[++$count];
805
806
                                if (ord($s[$count+1]) >= ord('0') &&
807
                                    ord($s[$count+1]) <= ord('9')) {
808
                                    $oct .= $s[++$count];
809
                                }
810
                            }
811
812
                            $out .= chr(octdec($oct));
813
                        } else {
814
                            $out .= $s[$count];
815
                        }
816
                }
817
            }
818
        }
819
        return $out;
820
    }
821
822
    /**
823
     * Hexadecimal to string
824
     *
825
     * @param string $hex
826
     * @return string
827
     */
828
    public function hex2str($hex) {
829
        return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
830
    }
831
832
    /**
833
     * String to hexadecimal
834
     *
835
     * @param string $str
836
     * @return string
837
     */
838
    public function str2hex($str) {
839
        return current(unpack('H*', $str));
840
    }
841
}
842