Completed
Branch develop (c3c152)
by
unknown
24:51
created
htdocs/includes/tcpdi/tcpdi_parser.php 1 patch
Indentation   +1351 added lines, -1351 removed lines patch added patch discarded remove patch
@@ -52,31 +52,31 @@  discard block
 block discarded – undo
52 52
 else require_once(dirname(__FILE__).'/../tecnickcom/tcpdf/include/tcpdf_filters.php');
53 53
 
54 54
 if (!defined ('PDF_TYPE_NULL'))
55
-    define ('PDF_TYPE_NULL', 0);
55
+	define ('PDF_TYPE_NULL', 0);
56 56
 if (!defined ('PDF_TYPE_NUMERIC'))
57
-    define ('PDF_TYPE_NUMERIC', 1);
57
+	define ('PDF_TYPE_NUMERIC', 1);
58 58
 if (!defined ('PDF_TYPE_TOKEN'))
59
-    define ('PDF_TYPE_TOKEN', 2);
59
+	define ('PDF_TYPE_TOKEN', 2);
60 60
 if (!defined ('PDF_TYPE_HEX'))
61
-    define ('PDF_TYPE_HEX', 3);
61
+	define ('PDF_TYPE_HEX', 3);
62 62
 if (!defined ('PDF_TYPE_STRING'))
63
-    define ('PDF_TYPE_STRING', 4);
63
+	define ('PDF_TYPE_STRING', 4);
64 64
 if (!defined ('PDF_TYPE_DICTIONARY'))
65
-    define ('PDF_TYPE_DICTIONARY', 5);
65
+	define ('PDF_TYPE_DICTIONARY', 5);
66 66
 if (!defined ('PDF_TYPE_ARRAY'))
67
-    define ('PDF_TYPE_ARRAY', 6);
67
+	define ('PDF_TYPE_ARRAY', 6);
68 68
 if (!defined ('PDF_TYPE_OBJDEC'))
69
-    define ('PDF_TYPE_OBJDEC', 7);
69
+	define ('PDF_TYPE_OBJDEC', 7);
70 70
 if (!defined ('PDF_TYPE_OBJREF'))
71
-    define ('PDF_TYPE_OBJREF', 8);
71
+	define ('PDF_TYPE_OBJREF', 8);
72 72
 if (!defined ('PDF_TYPE_OBJECT'))
73
-    define ('PDF_TYPE_OBJECT', 9);
73
+	define ('PDF_TYPE_OBJECT', 9);
74 74
 if (!defined ('PDF_TYPE_STREAM'))
75
-    define ('PDF_TYPE_STREAM', 10);
75
+	define ('PDF_TYPE_STREAM', 10);
76 76
 if (!defined ('PDF_TYPE_BOOLEAN'))
77
-    define ('PDF_TYPE_BOOLEAN', 11);
77
+	define ('PDF_TYPE_BOOLEAN', 11);
78 78
 if (!defined ('PDF_TYPE_REAL'))
79
-    define ('PDF_TYPE_REAL', 12);
79
+	define ('PDF_TYPE_REAL', 12);
80 80
 
81 81
 /**
82 82
  * @class tcpdi_parser
@@ -88,1362 +88,1362 @@  discard block
 block discarded – undo
88 88
  * @author Nicola Asuni - [email protected]
89 89
  */
90 90
 class tcpdi_parser {
91
-    /**
92
-     * Unique parser ID
93
-     * @public
94
-     */
95
-    public $uniqueid = '';
96
-
97
-    /**
98
-     * Raw content of the PDF document.
99
-     * @private
100
-     */
101
-    private $pdfdata = '';
102
-
103
-    /**
104
-     * XREF data.
105
-     * @protected
106
-     */
107
-    protected $xref = array();
108
-
109
-    /**
110
-     * Object streams.
111
-     * @protected
112
-     */
113
-    protected $objstreams = array();
114
-
115
-    /**
116
-     * Objects in objstreams.
117
-     * @protected
118
-     */
119
-    protected $objstreamobjs = array();
120
-
121
-    /**
122
-     * List of seen XREF data locations.
123
-     * @protected
124
-     */
125
-    protected $xref_seen_offsets = array();
126
-
127
-    /**
128
-     * Array of PDF objects.
129
-     * @protected
130
-     */
131
-    protected $objects = array();
132
-
133
-    /**
134
-     * Array of object offsets.
135
-     * @private
136
-     */
137
-    private $objoffsets = array();
138
-
139
-    /**
140
-     * Class object for decoding filters.
141
-     * @private
142
-     */
143
-    private $FilterDecoders;
144
-
145
-    /**
146
-     * Pages
147
-     *
148
-     * @private array
149
-     */
150
-    private $pages;
151
-
152
-    /**
153
-     * Page count
154
-     * @private integer
155
-     */
156
-    private $page_count;
157
-
158
-    /**
159
-     * actual page number
160
-     * @private integer
161
-     */
162
-    private $pageno;
163
-
164
-    /**
165
-     * PDF version of the loaded document
166
-     * @private string
167
-     */
168
-    private $pdfVersion;
169
-
170
-    /**
171
-     * Available BoxTypes
172
-     *
173
-     * @public array
174
-     */
175
-    public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');
91
+	/**
92
+	 * Unique parser ID
93
+	 * @public
94
+	 */
95
+	public $uniqueid = '';
96
+
97
+	/**
98
+	 * Raw content of the PDF document.
99
+	 * @private
100
+	 */
101
+	private $pdfdata = '';
102
+
103
+	/**
104
+	 * XREF data.
105
+	 * @protected
106
+	 */
107
+	protected $xref = array();
108
+
109
+	/**
110
+	 * Object streams.
111
+	 * @protected
112
+	 */
113
+	protected $objstreams = array();
114
+
115
+	/**
116
+	 * Objects in objstreams.
117
+	 * @protected
118
+	 */
119
+	protected $objstreamobjs = array();
120
+
121
+	/**
122
+	 * List of seen XREF data locations.
123
+	 * @protected
124
+	 */
125
+	protected $xref_seen_offsets = array();
126
+
127
+	/**
128
+	 * Array of PDF objects.
129
+	 * @protected
130
+	 */
131
+	protected $objects = array();
132
+
133
+	/**
134
+	 * Array of object offsets.
135
+	 * @private
136
+	 */
137
+	private $objoffsets = array();
138
+
139
+	/**
140
+	 * Class object for decoding filters.
141
+	 * @private
142
+	 */
143
+	private $FilterDecoders;
144
+
145
+	/**
146
+	 * Pages
147
+	 *
148
+	 * @private array
149
+	 */
150
+	private $pages;
151
+
152
+	/**
153
+	 * Page count
154
+	 * @private integer
155
+	 */
156
+	private $page_count;
157
+
158
+	/**
159
+	 * actual page number
160
+	 * @private integer
161
+	 */
162
+	private $pageno;
163
+
164
+	/**
165
+	 * PDF version of the loaded document
166
+	 * @private string
167
+	 */
168
+	private $pdfVersion;
169
+
170
+	/**
171
+	 * Available BoxTypes
172
+	 *
173
+	 * @public array
174
+	 */
175
+	public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');
176 176
 
177 177
 // -----------------------------------------------------------------------------
178 178
 
179
-    /**
180
-     * Parse a PDF document an return an array of objects.
181
-     * @param $data (string) PDF data to parse.
182
-     * @public
183
-     * @since 1.0.000 (2011-05-24)
184
-     */
185
-    public function __construct($data, $uniqueid) {
186
-        if (empty($data)) {
187
-            $this->Error('Empty PDF data.');
188
-        }
189
-        $this->uniqueid = $uniqueid;
190
-        $this->pdfdata = $data;
191
-        // get length
192
-        $pdflen = strlen($this->pdfdata);
193
-        // initialize class for decoding filters
194
-        $this->FilterDecoders = new TCPDF_FILTERS();
195
-        // get xref and trailer data
196
-        $this->xref = $this->getXrefData();
197
-        $this->findObjectOffsets();
198
-        // parse all document objects
199
-        $this->objects = array();
200
-        /*foreach ($this->xref['xref'] as $obj => $offset) {
179
+	/**
180
+	 * Parse a PDF document an return an array of objects.
181
+	 * @param $data (string) PDF data to parse.
182
+	 * @public
183
+	 * @since 1.0.000 (2011-05-24)
184
+	 */
185
+	public function __construct($data, $uniqueid) {
186
+		if (empty($data)) {
187
+			$this->Error('Empty PDF data.');
188
+		}
189
+		$this->uniqueid = $uniqueid;
190
+		$this->pdfdata = $data;
191
+		// get length
192
+		$pdflen = strlen($this->pdfdata);
193
+		// initialize class for decoding filters
194
+		$this->FilterDecoders = new TCPDF_FILTERS();
195
+		// get xref and trailer data
196
+		$this->xref = $this->getXrefData();
197
+		$this->findObjectOffsets();
198
+		// parse all document objects
199
+		$this->objects = array();
200
+		/*foreach ($this->xref['xref'] as $obj => $offset) {
201 201
             if (!isset($this->objects[$obj]) AND ($offset > 0)) {
202 202
                 // decode only objects with positive offset
203 203
                 //$this->objects[$obj] = $this->getIndirectObject($obj, $offset, true);
204 204
             }
205 205
         }*/
206
-        $this->getPDFVersion();
207
-        $this->readPages();
208
-    }
209
-
210
-    /**
211
-     * Clean up when done, to free memory etc
212
-     */
213
-    public function cleanUp() {
214
-        unset($this->pdfdata);
215
-        $this->pdfdata = '';
216
-        unset($this->objstreams);
217
-        $this->objstreams = array();
218
-        unset($this->objects);
219
-        $this->objects = array();
220
-        unset($this->objstreamobjs);
221
-        $this->objstreamobjs = array();
222
-        unset($this->xref);
223
-        $this->xref = array();
224
-        unset($this->objoffsets);
225
-        $this->objoffsets = array();
226
-        unset($this->pages);
227
-        $this->pages = array();
228
-    }
229
-
230
-    /**
231
-     * Return an array of parsed PDF document objects.
232
-     * @return (array) Array of parsed PDF document objects.
233
-     * @public
234
-     * @since 1.0.000 (2011-06-26)
235
-     */
236
-    public function getParsedData() {
237
-        return array($this->xref, $this->objects, $this->pages);
238
-    }
239
-
240
-    /**
241
-     * Get PDF-Version
242
-     *
243
-     * And reset the PDF Version used in FPDI if needed
244
-     * @public
245
-     */
246
-    public function getPDFVersion() {
247
-        preg_match('/\d\.\d/', substr($this->pdfdata, 0, 16), $m);
248
-        if (isset($m[0]))
249
-            $this->pdfVersion = $m[0];
250
-        return $this->pdfVersion;
251
-    }
252
-
253
-    /**
254
-     * Read all /Page(es)
255
-     *
256
-     */
257
-    function readPages() {
258
-        $params = $this->getObjectVal($this->xref['trailer'][1]['/Root']);
259
-        $objref = null;
260
-        foreach ($params[1][1] as $k=>$v) {
261
-            if ($k == '/Pages') {
262
-                $objref = $v;
263
-                break;
264
-            }
265
-        }
266
-        if ($objref == null || $objref[0] !== PDF_TYPE_OBJREF) {
267
-            // Offset not found.
268
-            return;
269
-        }
270
-
271
-        $dict = $this->getObjectVal($objref);
272
-        if ($dict[0] == PDF_TYPE_OBJECT && $dict[1][0] == PDF_TYPE_DICTIONARY) {
273
-            // Dict wrapped in an object
274
-            $dict = $dict[1];
275
-        }
276
-
277
-        if ($dict[0] !== PDF_TYPE_DICTIONARY) {
278
-            return;
279
-        }
280
-
281
-        $this->pages = array();
282
-        if (isset($dict[1]['/Kids'])) {
283
-            $v = $dict[1]['/Kids'];
284
-            if ($v[0] == PDF_TYPE_ARRAY) {
285
-                foreach ($v[1] as $ref) {
286
-                    $page = $this->getObjectVal($ref);
287
-                    $this->readPage($page);
288
-                }
289
-            }
290
-        }
291
-
292
-        $this->page_count = count($this->pages);
293
-    }
294
-
295
-    /**
296
-     * Read a single /Page element, recursing through /Kids if necessary
297
-     *
298
-     */
299
-    private function readPage($page) {
300
-        if (isset($page[1][1]['/Kids'])) {
301
-            // Nested pages!
302
-            foreach ($page[1][1]['/Kids'][1] as $subref) {
303
-                $subpage = $this->getObjectVal($subref);
304
-                $this->readPage($subpage);
305
-            }
306
-        } else {
307
-            $this->pages[] = $page;
308
-        }
309
-    }
310
-
311
-    /**
312
-     * Get pagecount from sourcefile
313
-     *
314
-     * @return int
315
-     */
316
-    function getPageCount() {
317
-        return $this->page_count;
318
-    }
319
-
320
-    /**
321
-     * Get Cross-Reference (xref) table and trailer data from PDF document data.
322
-     * @param $offset (int) xref offset (if know).
323
-     * @param $xref (array) previous xref array (if any).
324
-     * @return Array containing xref and trailer data.
325
-     * @protected
326
-     * @since 1.0.000 (2011-05-24)
327
-     */
328
-    protected function getXrefData($offset=0, $xref=array()) {
329
-        if ($offset == 0) {
330
-            // find last startxref
331
-            if (preg_match('/.*[\r\n]startxref[\s\r\n]+([0-9]+)[\s\r\n]+%%EOF/is', $this->pdfdata, $matches) == 0) {
332
-                $this->Error('Unable to find startxref');
333
-            }
334
-            $startxref = $matches[1];
335
-        } else {
336
-            if (preg_match('/([0-9]+[\s][0-9]+[\s]obj)/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset)) {
337
-                // Cross-Reference Stream object
338
-                $startxref = $offset;
339
-            } elseif (preg_match('/[\r\n]startxref[\s\r\n]+([0-9]+)[\s\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset)) {
340
-                // startxref found
341
-                $startxref = $matches[1][0];
342
-            } else {
343
-                $this->Error('Unable to find startxref');
344
-            }
345
-        }
346
-        unset($matches);
347
-
348
-        // DOMPDF gets the startxref wrong, giving us the linebreak before the xref starts.
349
-        $startxref += strspn($this->pdfdata, "\r\n", $startxref);
350
-
351
-        // check xref position
352
-        if (strpos($this->pdfdata, 'xref', $startxref) == $startxref) {
353
-            // Cross-Reference
354
-            $xref = $this->decodeXref($startxref, $xref);
355
-        } else {
356
-            // Cross-Reference Stream
357
-            $xref = $this->decodeXrefStream($startxref, $xref);
358
-        }
359
-        if (empty($xref)) {
360
-            $this->Error('Unable to find xref');
361
-        }
362
-
363
-        return $xref;
364
-    }
365
-
366
-    /**
367
-     * Decode the Cross-Reference section
368
-     * @param $startxref (int) Offset at which the xref section starts.
369
-     * @param $xref (array) Previous xref array (if any).
370
-     * @return Array containing xref and trailer data.
371
-     * @protected
372
-     * @since 1.0.000 (2011-06-20)
373
-     */
374
-    protected function decodeXref($startxref, $xref=array()) {
375
-        $this->xref_seen_offsets[] = $startxref;
376
-        if (!isset($xref['xref_location'])) {
377
-            $xref['xref_location'] = $startxref;
378
-            $xref['max_object'] = 0;
379
-        }
380
-        // extract xref data (object indexes and offsets)
381
-        $xoffset = $startxref + 5;
382
-        // initialize object number
383
-        $obj_num = 0;
384
-        $offset = $xoffset;
385
-        while (preg_match('/^([0-9]+)[\s]([0-9]+)[\s]?([nf]?)/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
386
-            $offset = (strlen($matches[0][0]) + $matches[0][1]);
387
-            if ($matches[3][0] == 'n') {
388
-                // create unique object index: [object number]_[generation number]
389
-                $gen_num = intval($matches[2][0]);
390
-                $index = $obj_num.'_'.$gen_num;
391
-                // check if object already exist
392
-                if (!isset($xref['xref'][$obj_num][$gen_num])) {
393
-                    // store object offset position
394
-                    $xref['xref'][$obj_num][$gen_num] = intval($matches[1][0]);
395
-                }
396
-                ++$obj_num;
397
-                $offset += 2;
398
-            } elseif ($matches[3][0] == 'f') {
399
-                ++$obj_num;
400
-                $offset += 2;
401
-            } else {
402
-                // object number (index)
403
-                $obj_num = intval($matches[1][0]);
404
-            }
405
-        }
406
-        unset($matches);
407
-        $xref['max_object'] = max($xref['max_object'], $obj_num);
408
-        // get trailer data
409
-        if (preg_match('/trailer[\s]*<<(.*)>>[\s\r\n]+(?:[%].*[\r\n]+)*startxref[\s\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $xoffset) > 0) {
410
-            $trailer_data = $matches[1][0];
411
-            if (!isset($xref['trailer']) OR empty($xref['trailer'])) {
412
-                // get only the last updated version
413
-                $xref['trailer'] = array();
414
-                $xref['trailer'][0] = PDF_TYPE_DICTIONARY;
415
-                $xref['trailer'][1] = array();
416
-                // parse trailer_data
417
-                if (preg_match('/Size[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
418
-                    $xref['trailer'][1]['/Size'] = array(PDF_TYPE_NUMERIC, intval($matches[1]));
419
-                }
420
-                if (preg_match('/Root[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
421
-                    $xref['trailer'][1]['/Root'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
422
-                }
423
-                if (preg_match('/Encrypt[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
424
-                    $xref['trailer'][1]['/Encrypt'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
425
-                }
426
-                if (preg_match('/Info[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
427
-                    $xref['trailer'][1]['/Info'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
428
-                }
429
-                if (preg_match('/ID[\s]*[\[][\s]*[<]([^>]*)[>][\s]*[<]([^>]*)[>]/i', $trailer_data, $matches) > 0) {
430
-                    $xref['trailer'][1]['/ID'] = array(PDF_TYPE_ARRAY, array());
431
-                    $xref['trailer'][1]['/ID'][1][0] = array(PDF_TYPE_HEX, $matches[1]);
432
-                    $xref['trailer'][1]['/ID'][1][1] = array(PDF_TYPE_HEX, $matches[2]);
433
-                }
434
-            }
435
-            if (preg_match('/Prev[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
436
-                // get previous xref
437
-                $prevoffset = intval($matches[1]);
438
-                if (!in_array($prevoffset, $this->xref_seen_offsets)) {
439
-                    $this->xref_seen_offsets[] = $prevoffset;
440
-                    $xref = $this->getXrefData($prevoffset, $xref);
441
-                }
442
-            }
443
-            unset($matches);
444
-        } else {
445
-            $this->Error('Unable to find trailer');
446
-        }
447
-        return $xref;
448
-    }
449
-
450
-    /**
451
-     * Decode the Cross-Reference Stream section
452
-     * @param $startxref (int) Offset at which the xref section starts.
453
-     * @param $xref (array) Previous xref array (if any).
454
-     * @return Array containing xref and trailer data.
455
-     * @protected
456
-     * @since 1.0.003 (2013-03-16)
457
-     */
458
-    protected function decodeXrefStream($startxref, $xref=array()) {
459
-        // try to read Cross-Reference Stream
460
-        list($xrefobj, $unused) = $this->getRawObject($startxref);
461
-        $xrefcrs = $this->getIndirectObject($xrefobj[1], $startxref, true);
462
-        if (!isset($xref['xref_location'])) {
463
-            $xref['xref_location'] = $startxref;
464
-            $xref['max_object'] = 0;
465
-        }
466
-        if (!isset($xref['xref'])) {
467
-            $xref['xref'] = array();
468
-        }
469
-        if (!isset($xref['trailer']) OR empty($xref['trailer'])) {
470
-            // get only the last updated version
471
-            $xref['trailer'] = array();
472
-            $xref['trailer'][0] = PDF_TYPE_DICTIONARY;
473
-            $xref['trailer'][1] = array();
474
-            $filltrailer = true;
475
-        } else {
476
-            $filltrailer = false;
477
-        }
478
-        $valid_crs = false;
479
-        $sarr = $xrefcrs[0][1];
480
-        $keys = array_keys($sarr);
481
-        $columns = 1; // Default as per PDF 32000-1:2008.
482
-        $predictor = 1; // Default as per PDF 32000-1:2008.
483
-        foreach ($keys as $k=>$key) {
484
-            $v = $sarr[$key];
485
-            if (($key == '/Type') AND ($v[0] == PDF_TYPE_TOKEN AND ($v[1] == 'XRef'))) {
486
-                $valid_crs = true;
487
-            } elseif (($key == '/Index') AND ($v[0] == PDF_TYPE_ARRAY AND count($v[1]) >= 2)) {
488
-                // first object number in the subsection
489
-                $index_first = intval($v[1][0][1]);
490
-                // number of entries in the subsection
491
-                $index_entries = intval($v[1][1][1]);
492
-            } elseif (($key == '/Prev') AND ($v[0] == PDF_TYPE_NUMERIC)) {
493
-                // get previous xref offset
494
-                $prevxref = intval($v[1]);
495
-            } elseif (($key == '/W') AND ($v[0] == PDF_TYPE_ARRAY)) {
496
-                // number of bytes (in the decoded stream) of the corresponding field
497
-                $wb = array();
498
-                $wb[0] = intval($v[1][0][1]);
499
-                $wb[1] = intval($v[1][1][1]);
500
-                $wb[2] = intval($v[1][2][1]);
501
-            } elseif (($key == '/DecodeParms') AND ($v[0] == PDF_TYPE_DICTIONARY)) {
502
-                $decpar = $v[1];
503
-                foreach ($decpar as $kdc => $vdc) {
504
-                    if (($kdc == '/Columns') AND ($vdc[0] == PDF_TYPE_NUMERIC)) {
505
-                        $columns = intval($vdc[1]);
506
-                    } elseif (($kdc == '/Predictor') AND ($vdc[0] == PDF_TYPE_NUMERIC)) {
507
-                        $predictor = intval($vdc[1]);
508
-                    }
509
-                }
510
-            } elseif ($filltrailer) {
511
-                switch($key) {
512
-                    case '/Size':
513
-                    case '/Root':
514
-                    case '/Info':
515
-                    case '/ID':
516
-                        $xref['trailer'][1][$key] = $v;
517
-                        break;
518
-                    default:
519
-                        break;
520
-                }
521
-            }
522
-        }
523
-        // decode data
524
-        $obj_num = 0;
525
-        if ($valid_crs AND isset($xrefcrs[1][3][0])) {
526
-            // number of bytes in a row
527
-            $rowlen = ($columns + 1);
528
-            // convert the stream into an array of integers
529
-            $sdata = unpack('C*', $xrefcrs[1][3][0]);
530
-            // split the rows
531
-            $sdata = array_chunk($sdata, $rowlen);
532
-            // initialize decoded array
533
-            $ddata = array();
534
-            // initialize first row with zeros
535
-            $prev_row = array_fill (0, $rowlen, 0);
536
-            // for each row apply PNG unpredictor
537
-            foreach ($sdata as $k => $row) {
538
-                // initialize new row
539
-                $ddata[$k] = array();
540
-                // get PNG predictor value
541
-                if (empty($predictor)) {
542
-                    $predictor = (10 + $row[0]);
543
-                }
544
-                // for each byte on the row
545
-                for ($i=1; $i<=$columns; ++$i) {
546
-                    if (!isset($row[$i])) {
547
-                        // No more data in this row - we're done here.
548
-                        break;
549
-                    }
550
-                    // new index
551
-                    $j = ($i - 1);
552
-                    $row_up = $prev_row[$j];
553
-                    if ($i == 1) {
554
-                        $row_left = 0;
555
-                        $row_upleft = 0;
556
-                    } else {
557
-                        $row_left = $row[($i - 1)];
558
-                        $row_upleft = $prev_row[($j - 1)];
559
-                    }
560
-                    switch ($predictor) {
561
-                        case 1: // No prediction (equivalent to PNG None)
562
-                        case 10: { // PNG prediction (on encoding, PNG None on all rows)
563
-                            $ddata[$k][$j] = $row[$i];
564
-                            break;
565
-                        }
566
-                        case 11: { // PNG prediction (on encoding, PNG Sub on all rows)
567
-                            $ddata[$k][$j] = (($row[$i] + $row_left) & 0xff);
568
-                            break;
569
-                        }
570
-                        case 12: { // PNG prediction (on encoding, PNG Up on all rows)
571
-                            $ddata[$k][$j] = (($row[$i] + $row_up) & 0xff);
572
-                            break;
573
-                        }
574
-                        case 13: { // PNG prediction (on encoding, PNG Average on all rows)
575
-                            $ddata[$k][$j] = (($row[$i] + (($row_left + $row_up) / 2)) & 0xff);
576
-                            break;
577
-                        }
578
-                        case 14: { // PNG prediction (on encoding, PNG Paeth on all rows)
579
-                            // initial estimate
580
-                            $p = ($row_left + $row_up - $row_upleft);
581
-                            // distances
582
-                            $pa = abs($p - $row_left);
583
-                            $pb = abs($p - $row_up);
584
-                            $pc = abs($p - $row_upleft);
585
-                            $pmin = min($pa, $pb, $pc);
586
-                            // return minumum distance
587
-                            switch ($pmin) {
588
-                                case $pa: {
589
-                                    $ddata[$k][$j] = (($row[$i] + $row_left) & 0xff);
590
-                                    break;
591
-                                }
592
-                                case $pb: {
593
-                                    $ddata[$k][$j] = (($row[$i] + $row_up) & 0xff);
594
-                                    break;
595
-                                }
596
-                                case $pc: {
597
-                                    $ddata[$k][$j] = (($row[$i] + $row_upleft) & 0xff);
598
-                                    break;
599
-                                }
600
-                            }
601
-                            break;
602
-                        }
603
-                        default: { // PNG prediction (on encoding, PNG optimum)
604
-                            $this->Error("Unknown PNG predictor $predictor");
605
-                            break;
606
-                        }
607
-                    }
608
-                }
609
-                $prev_row = $ddata[$k];
610
-            } // end for each row
611
-            // complete decoding
612
-            unset($sdata);
613
-            $sdata = array();
614
-            // for every row
615
-            foreach ($ddata as $k => $row) {
616
-                // initialize new row
617
-                $sdata[$k] = array(0, 0, 0);
618
-                if ($wb[0] == 0) {
619
-                    // default type field
620
-                    $sdata[$k][0] = 1;
621
-                }
622
-                $i = 0; // count bytes on the row
623
-                // for every column
624
-                for ($c = 0; $c < 3; ++$c) {
625
-                    // for every byte on the column
626
-                    for ($b = 0; $b < $wb[$c]; ++$b) {
627
-                        if (isset($row[$i])) {
628
-                            $sdata[$k][$c] += ($row[$i] << (($wb[$c] - 1 - $b) * 8));
629
-                        }
630
-                        ++$i;
631
-                    }
632
-                }
633
-            }
634
-            unset($ddata);
635
-            // fill xref
636
-            if (isset($index_first)) {
637
-                $obj_num = $index_first;
638
-            } else {
639
-                $obj_num = 0;
640
-            }
641
-            foreach ($sdata as $k => $row) {
642
-                switch ($row[0]) {
643
-                    case 0: { // (f) linked list of free objects
644
-                        ++$obj_num;
645
-                        break;
646
-                    }
647
-                    case 1: { // (n) objects that are in use but are not compressed
648
-                        // create unique object index: [object number]_[generation number]
649
-                        $index = $obj_num.'_'.$row[2];
650
-                        // check if object already exist
651
-                        if (!isset($xref['xref'][$obj_num][$row[2]])) {
652
-                            // store object offset position
653
-                            $xref['xref'][$obj_num][$row[2]] = $row[1];
654
-                        }
655
-                        ++$obj_num;
656
-                        break;
657
-                    }
658
-                    case 2: { // compressed objects
659
-                        // $row[1] = object number of the object stream in which this object is stored
660
-                        // $row[2] = index of this object within the object stream
661
-                        /*$index = $row[1].'_0_'.$row[2];
206
+		$this->getPDFVersion();
207
+		$this->readPages();
208
+	}
209
+
210
+	/**
211
+	 * Clean up when done, to free memory etc
212
+	 */
213
+	public function cleanUp() {
214
+		unset($this->pdfdata);
215
+		$this->pdfdata = '';
216
+		unset($this->objstreams);
217
+		$this->objstreams = array();
218
+		unset($this->objects);
219
+		$this->objects = array();
220
+		unset($this->objstreamobjs);
221
+		$this->objstreamobjs = array();
222
+		unset($this->xref);
223
+		$this->xref = array();
224
+		unset($this->objoffsets);
225
+		$this->objoffsets = array();
226
+		unset($this->pages);
227
+		$this->pages = array();
228
+	}
229
+
230
+	/**
231
+	 * Return an array of parsed PDF document objects.
232
+	 * @return (array) Array of parsed PDF document objects.
233
+	 * @public
234
+	 * @since 1.0.000 (2011-06-26)
235
+	 */
236
+	public function getParsedData() {
237
+		return array($this->xref, $this->objects, $this->pages);
238
+	}
239
+
240
+	/**
241
+	 * Get PDF-Version
242
+	 *
243
+	 * And reset the PDF Version used in FPDI if needed
244
+	 * @public
245
+	 */
246
+	public function getPDFVersion() {
247
+		preg_match('/\d\.\d/', substr($this->pdfdata, 0, 16), $m);
248
+		if (isset($m[0]))
249
+			$this->pdfVersion = $m[0];
250
+		return $this->pdfVersion;
251
+	}
252
+
253
+	/**
254
+	 * Read all /Page(es)
255
+	 *
256
+	 */
257
+	function readPages() {
258
+		$params = $this->getObjectVal($this->xref['trailer'][1]['/Root']);
259
+		$objref = null;
260
+		foreach ($params[1][1] as $k=>$v) {
261
+			if ($k == '/Pages') {
262
+				$objref = $v;
263
+				break;
264
+			}
265
+		}
266
+		if ($objref == null || $objref[0] !== PDF_TYPE_OBJREF) {
267
+			// Offset not found.
268
+			return;
269
+		}
270
+
271
+		$dict = $this->getObjectVal($objref);
272
+		if ($dict[0] == PDF_TYPE_OBJECT && $dict[1][0] == PDF_TYPE_DICTIONARY) {
273
+			// Dict wrapped in an object
274
+			$dict = $dict[1];
275
+		}
276
+
277
+		if ($dict[0] !== PDF_TYPE_DICTIONARY) {
278
+			return;
279
+		}
280
+
281
+		$this->pages = array();
282
+		if (isset($dict[1]['/Kids'])) {
283
+			$v = $dict[1]['/Kids'];
284
+			if ($v[0] == PDF_TYPE_ARRAY) {
285
+				foreach ($v[1] as $ref) {
286
+					$page = $this->getObjectVal($ref);
287
+					$this->readPage($page);
288
+				}
289
+			}
290
+		}
291
+
292
+		$this->page_count = count($this->pages);
293
+	}
294
+
295
+	/**
296
+	 * Read a single /Page element, recursing through /Kids if necessary
297
+	 *
298
+	 */
299
+	private function readPage($page) {
300
+		if (isset($page[1][1]['/Kids'])) {
301
+			// Nested pages!
302
+			foreach ($page[1][1]['/Kids'][1] as $subref) {
303
+				$subpage = $this->getObjectVal($subref);
304
+				$this->readPage($subpage);
305
+			}
306
+		} else {
307
+			$this->pages[] = $page;
308
+		}
309
+	}
310
+
311
+	/**
312
+	 * Get pagecount from sourcefile
313
+	 *
314
+	 * @return int
315
+	 */
316
+	function getPageCount() {
317
+		return $this->page_count;
318
+	}
319
+
320
+	/**
321
+	 * Get Cross-Reference (xref) table and trailer data from PDF document data.
322
+	 * @param $offset (int) xref offset (if know).
323
+	 * @param $xref (array) previous xref array (if any).
324
+	 * @return Array containing xref and trailer data.
325
+	 * @protected
326
+	 * @since 1.0.000 (2011-05-24)
327
+	 */
328
+	protected function getXrefData($offset=0, $xref=array()) {
329
+		if ($offset == 0) {
330
+			// find last startxref
331
+			if (preg_match('/.*[\r\n]startxref[\s\r\n]+([0-9]+)[\s\r\n]+%%EOF/is', $this->pdfdata, $matches) == 0) {
332
+				$this->Error('Unable to find startxref');
333
+			}
334
+			$startxref = $matches[1];
335
+		} else {
336
+			if (preg_match('/([0-9]+[\s][0-9]+[\s]obj)/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset)) {
337
+				// Cross-Reference Stream object
338
+				$startxref = $offset;
339
+			} elseif (preg_match('/[\r\n]startxref[\s\r\n]+([0-9]+)[\s\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset)) {
340
+				// startxref found
341
+				$startxref = $matches[1][0];
342
+			} else {
343
+				$this->Error('Unable to find startxref');
344
+			}
345
+		}
346
+		unset($matches);
347
+
348
+		// DOMPDF gets the startxref wrong, giving us the linebreak before the xref starts.
349
+		$startxref += strspn($this->pdfdata, "\r\n", $startxref);
350
+
351
+		// check xref position
352
+		if (strpos($this->pdfdata, 'xref', $startxref) == $startxref) {
353
+			// Cross-Reference
354
+			$xref = $this->decodeXref($startxref, $xref);
355
+		} else {
356
+			// Cross-Reference Stream
357
+			$xref = $this->decodeXrefStream($startxref, $xref);
358
+		}
359
+		if (empty($xref)) {
360
+			$this->Error('Unable to find xref');
361
+		}
362
+
363
+		return $xref;
364
+	}
365
+
366
+	/**
367
+	 * Decode the Cross-Reference section
368
+	 * @param $startxref (int) Offset at which the xref section starts.
369
+	 * @param $xref (array) Previous xref array (if any).
370
+	 * @return Array containing xref and trailer data.
371
+	 * @protected
372
+	 * @since 1.0.000 (2011-06-20)
373
+	 */
374
+	protected function decodeXref($startxref, $xref=array()) {
375
+		$this->xref_seen_offsets[] = $startxref;
376
+		if (!isset($xref['xref_location'])) {
377
+			$xref['xref_location'] = $startxref;
378
+			$xref['max_object'] = 0;
379
+		}
380
+		// extract xref data (object indexes and offsets)
381
+		$xoffset = $startxref + 5;
382
+		// initialize object number
383
+		$obj_num = 0;
384
+		$offset = $xoffset;
385
+		while (preg_match('/^([0-9]+)[\s]([0-9]+)[\s]?([nf]?)/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
386
+			$offset = (strlen($matches[0][0]) + $matches[0][1]);
387
+			if ($matches[3][0] == 'n') {
388
+				// create unique object index: [object number]_[generation number]
389
+				$gen_num = intval($matches[2][0]);
390
+				$index = $obj_num.'_'.$gen_num;
391
+				// check if object already exist
392
+				if (!isset($xref['xref'][$obj_num][$gen_num])) {
393
+					// store object offset position
394
+					$xref['xref'][$obj_num][$gen_num] = intval($matches[1][0]);
395
+				}
396
+				++$obj_num;
397
+				$offset += 2;
398
+			} elseif ($matches[3][0] == 'f') {
399
+				++$obj_num;
400
+				$offset += 2;
401
+			} else {
402
+				// object number (index)
403
+				$obj_num = intval($matches[1][0]);
404
+			}
405
+		}
406
+		unset($matches);
407
+		$xref['max_object'] = max($xref['max_object'], $obj_num);
408
+		// get trailer data
409
+		if (preg_match('/trailer[\s]*<<(.*)>>[\s\r\n]+(?:[%].*[\r\n]+)*startxref[\s\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $xoffset) > 0) {
410
+			$trailer_data = $matches[1][0];
411
+			if (!isset($xref['trailer']) OR empty($xref['trailer'])) {
412
+				// get only the last updated version
413
+				$xref['trailer'] = array();
414
+				$xref['trailer'][0] = PDF_TYPE_DICTIONARY;
415
+				$xref['trailer'][1] = array();
416
+				// parse trailer_data
417
+				if (preg_match('/Size[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
418
+					$xref['trailer'][1]['/Size'] = array(PDF_TYPE_NUMERIC, intval($matches[1]));
419
+				}
420
+				if (preg_match('/Root[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
421
+					$xref['trailer'][1]['/Root'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
422
+				}
423
+				if (preg_match('/Encrypt[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
424
+					$xref['trailer'][1]['/Encrypt'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
425
+				}
426
+				if (preg_match('/Info[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) {
427
+					$xref['trailer'][1]['/Info'] = array(PDF_TYPE_OBJREF, intval($matches[1]), intval($matches[2]));
428
+				}
429
+				if (preg_match('/ID[\s]*[\[][\s]*[<]([^>]*)[>][\s]*[<]([^>]*)[>]/i', $trailer_data, $matches) > 0) {
430
+					$xref['trailer'][1]['/ID'] = array(PDF_TYPE_ARRAY, array());
431
+					$xref['trailer'][1]['/ID'][1][0] = array(PDF_TYPE_HEX, $matches[1]);
432
+					$xref['trailer'][1]['/ID'][1][1] = array(PDF_TYPE_HEX, $matches[2]);
433
+				}
434
+			}
435
+			if (preg_match('/Prev[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
436
+				// get previous xref
437
+				$prevoffset = intval($matches[1]);
438
+				if (!in_array($prevoffset, $this->xref_seen_offsets)) {
439
+					$this->xref_seen_offsets[] = $prevoffset;
440
+					$xref = $this->getXrefData($prevoffset, $xref);
441
+				}
442
+			}
443
+			unset($matches);
444
+		} else {
445
+			$this->Error('Unable to find trailer');
446
+		}
447
+		return $xref;
448
+	}
449
+
450
+	/**
451
+	 * Decode the Cross-Reference Stream section
452
+	 * @param $startxref (int) Offset at which the xref section starts.
453
+	 * @param $xref (array) Previous xref array (if any).
454
+	 * @return Array containing xref and trailer data.
455
+	 * @protected
456
+	 * @since 1.0.003 (2013-03-16)
457
+	 */
458
+	protected function decodeXrefStream($startxref, $xref=array()) {
459
+		// try to read Cross-Reference Stream
460
+		list($xrefobj, $unused) = $this->getRawObject($startxref);
461
+		$xrefcrs = $this->getIndirectObject($xrefobj[1], $startxref, true);
462
+		if (!isset($xref['xref_location'])) {
463
+			$xref['xref_location'] = $startxref;
464
+			$xref['max_object'] = 0;
465
+		}
466
+		if (!isset($xref['xref'])) {
467
+			$xref['xref'] = array();
468
+		}
469
+		if (!isset($xref['trailer']) OR empty($xref['trailer'])) {
470
+			// get only the last updated version
471
+			$xref['trailer'] = array();
472
+			$xref['trailer'][0] = PDF_TYPE_DICTIONARY;
473
+			$xref['trailer'][1] = array();
474
+			$filltrailer = true;
475
+		} else {
476
+			$filltrailer = false;
477
+		}
478
+		$valid_crs = false;
479
+		$sarr = $xrefcrs[0][1];
480
+		$keys = array_keys($sarr);
481
+		$columns = 1; // Default as per PDF 32000-1:2008.
482
+		$predictor = 1; // Default as per PDF 32000-1:2008.
483
+		foreach ($keys as $k=>$key) {
484
+			$v = $sarr[$key];
485
+			if (($key == '/Type') AND ($v[0] == PDF_TYPE_TOKEN AND ($v[1] == 'XRef'))) {
486
+				$valid_crs = true;
487
+			} elseif (($key == '/Index') AND ($v[0] == PDF_TYPE_ARRAY AND count($v[1]) >= 2)) {
488
+				// first object number in the subsection
489
+				$index_first = intval($v[1][0][1]);
490
+				// number of entries in the subsection
491
+				$index_entries = intval($v[1][1][1]);
492
+			} elseif (($key == '/Prev') AND ($v[0] == PDF_TYPE_NUMERIC)) {
493
+				// get previous xref offset
494
+				$prevxref = intval($v[1]);
495
+			} elseif (($key == '/W') AND ($v[0] == PDF_TYPE_ARRAY)) {
496
+				// number of bytes (in the decoded stream) of the corresponding field
497
+				$wb = array();
498
+				$wb[0] = intval($v[1][0][1]);
499
+				$wb[1] = intval($v[1][1][1]);
500
+				$wb[2] = intval($v[1][2][1]);
501
+			} elseif (($key == '/DecodeParms') AND ($v[0] == PDF_TYPE_DICTIONARY)) {
502
+				$decpar = $v[1];
503
+				foreach ($decpar as $kdc => $vdc) {
504
+					if (($kdc == '/Columns') AND ($vdc[0] == PDF_TYPE_NUMERIC)) {
505
+						$columns = intval($vdc[1]);
506
+					} elseif (($kdc == '/Predictor') AND ($vdc[0] == PDF_TYPE_NUMERIC)) {
507
+						$predictor = intval($vdc[1]);
508
+					}
509
+				}
510
+			} elseif ($filltrailer) {
511
+				switch($key) {
512
+					case '/Size':
513
+					case '/Root':
514
+					case '/Info':
515
+					case '/ID':
516
+						$xref['trailer'][1][$key] = $v;
517
+						break;
518
+					default:
519
+						break;
520
+				}
521
+			}
522
+		}
523
+		// decode data
524
+		$obj_num = 0;
525
+		if ($valid_crs AND isset($xrefcrs[1][3][0])) {
526
+			// number of bytes in a row
527
+			$rowlen = ($columns + 1);
528
+			// convert the stream into an array of integers
529
+			$sdata = unpack('C*', $xrefcrs[1][3][0]);
530
+			// split the rows
531
+			$sdata = array_chunk($sdata, $rowlen);
532
+			// initialize decoded array
533
+			$ddata = array();
534
+			// initialize first row with zeros
535
+			$prev_row = array_fill (0, $rowlen, 0);
536
+			// for each row apply PNG unpredictor
537
+			foreach ($sdata as $k => $row) {
538
+				// initialize new row
539
+				$ddata[$k] = array();
540
+				// get PNG predictor value
541
+				if (empty($predictor)) {
542
+					$predictor = (10 + $row[0]);
543
+				}
544
+				// for each byte on the row
545
+				for ($i=1; $i<=$columns; ++$i) {
546
+					if (!isset($row[$i])) {
547
+						// No more data in this row - we're done here.
548
+						break;
549
+					}
550
+					// new index
551
+					$j = ($i - 1);
552
+					$row_up = $prev_row[$j];
553
+					if ($i == 1) {
554
+						$row_left = 0;
555
+						$row_upleft = 0;
556
+					} else {
557
+						$row_left = $row[($i - 1)];
558
+						$row_upleft = $prev_row[($j - 1)];
559
+					}
560
+					switch ($predictor) {
561
+						case 1: // No prediction (equivalent to PNG None)
562
+						case 10: { // PNG prediction (on encoding, PNG None on all rows)
563
+							$ddata[$k][$j] = $row[$i];
564
+							break;
565
+						}
566
+						case 11: { // PNG prediction (on encoding, PNG Sub on all rows)
567
+							$ddata[$k][$j] = (($row[$i] + $row_left) & 0xff);
568
+							break;
569
+						}
570
+						case 12: { // PNG prediction (on encoding, PNG Up on all rows)
571
+							$ddata[$k][$j] = (($row[$i] + $row_up) & 0xff);
572
+							break;
573
+						}
574
+						case 13: { // PNG prediction (on encoding, PNG Average on all rows)
575
+							$ddata[$k][$j] = (($row[$i] + (($row_left + $row_up) / 2)) & 0xff);
576
+							break;
577
+						}
578
+						case 14: { // PNG prediction (on encoding, PNG Paeth on all rows)
579
+							// initial estimate
580
+							$p = ($row_left + $row_up - $row_upleft);
581
+							// distances
582
+							$pa = abs($p - $row_left);
583
+							$pb = abs($p - $row_up);
584
+							$pc = abs($p - $row_upleft);
585
+							$pmin = min($pa, $pb, $pc);
586
+							// return minumum distance
587
+							switch ($pmin) {
588
+								case $pa: {
589
+									$ddata[$k][$j] = (($row[$i] + $row_left) & 0xff);
590
+									break;
591
+								}
592
+								case $pb: {
593
+									$ddata[$k][$j] = (($row[$i] + $row_up) & 0xff);
594
+									break;
595
+								}
596
+								case $pc: {
597
+									$ddata[$k][$j] = (($row[$i] + $row_upleft) & 0xff);
598
+									break;
599
+								}
600
+							}
601
+							break;
602
+						}
603
+						default: { // PNG prediction (on encoding, PNG optimum)
604
+							$this->Error("Unknown PNG predictor $predictor");
605
+							break;
606
+						}
607
+					}
608
+				}
609
+				$prev_row = $ddata[$k];
610
+			} // end for each row
611
+			// complete decoding
612
+			unset($sdata);
613
+			$sdata = array();
614
+			// for every row
615
+			foreach ($ddata as $k => $row) {
616
+				// initialize new row
617
+				$sdata[$k] = array(0, 0, 0);
618
+				if ($wb[0] == 0) {
619
+					// default type field
620
+					$sdata[$k][0] = 1;
621
+				}
622
+				$i = 0; // count bytes on the row
623
+				// for every column
624
+				for ($c = 0; $c < 3; ++$c) {
625
+					// for every byte on the column
626
+					for ($b = 0; $b < $wb[$c]; ++$b) {
627
+						if (isset($row[$i])) {
628
+							$sdata[$k][$c] += ($row[$i] << (($wb[$c] - 1 - $b) * 8));
629
+						}
630
+						++$i;
631
+					}
632
+				}
633
+			}
634
+			unset($ddata);
635
+			// fill xref
636
+			if (isset($index_first)) {
637
+				$obj_num = $index_first;
638
+			} else {
639
+				$obj_num = 0;
640
+			}
641
+			foreach ($sdata as $k => $row) {
642
+				switch ($row[0]) {
643
+					case 0: { // (f) linked list of free objects
644
+						++$obj_num;
645
+						break;
646
+					}
647
+					case 1: { // (n) objects that are in use but are not compressed
648
+						// create unique object index: [object number]_[generation number]
649
+						$index = $obj_num.'_'.$row[2];
650
+						// check if object already exist
651
+						if (!isset($xref['xref'][$obj_num][$row[2]])) {
652
+							// store object offset position
653
+							$xref['xref'][$obj_num][$row[2]] = $row[1];
654
+						}
655
+						++$obj_num;
656
+						break;
657
+					}
658
+					case 2: { // compressed objects
659
+						// $row[1] = object number of the object stream in which this object is stored
660
+						// $row[2] = index of this object within the object stream
661
+						/*$index = $row[1].'_0_'.$row[2];
662 662
                         $xref['xref'][$row[1]][0][$row[2]] = -1;*/
663
-                        break;
664
-                    }
665
-                    default: { // null objects
666
-                        break;
667
-                    }
668
-                }
669
-            }
670
-        } // end decoding data
671
-        $xref['max_object'] = max($xref['max_object'], $obj_num);
672
-        if (isset($prevxref)) {
673
-            // get previous xref
674
-            $xref = $this->getXrefData($prevxref, $xref);
675
-        }
676
-        return $xref;
677
-    }
678
-
679
-    /**
680
-     * Get raw stream data
681
-     * @param $offset (int) Stream offset.
682
-     * @param $length (int) Stream length.
683
-     * @return string Steam content
684
-     * @protected
685
-     */
686
-    protected function getRawStream($offset, $length) {
687
-        $offset += strspn($this->pdfdata, "\x00\x09\x0a\x0c\x0d\x20", $offset);
688
-        $offset += 6; // "stream"
689
-        $offset += strspn($this->pdfdata, "\x20", $offset);
690
-        $offset += strspn($this->pdfdata, "\r\n", $offset);
691
-
692
-        $obj = array();
693
-        $obj[] = PDF_TYPE_STREAM;
694
-        $obj[] = substr($this->pdfdata, $offset, $length);
695
-
696
-        return array($obj, $offset+$length);
697
-    }
698
-
699
-    /**
700
-     * Get object type, raw value and offset to next object
701
-     * @param $offset (int) Object offset.
702
-     * @return array containing object type, raw value and offset to next object
703
-     * @protected
704
-     * @since 1.0.000 (2011-06-20)
705
-     */
706
-    protected function getRawObject($offset=0, $data=null) {
707
-        if ($data == null) {
708
-            $data =& $this->pdfdata;
709
-        }
710
-        $objtype = ''; // object type to be returned
711
-        $objval = ''; // object value to be returned
712
-        // skip initial white space chars: \x00 null (NUL), \x09 horizontal tab (HT), \x0A line feed (LF), \x0C form feed (FF), \x0D carriage return (CR), \x20 space (SP)
663
+						break;
664
+					}
665
+					default: { // null objects
666
+						break;
667
+					}
668
+				}
669
+			}
670
+		} // end decoding data
671
+		$xref['max_object'] = max($xref['max_object'], $obj_num);
672
+		if (isset($prevxref)) {
673
+			// get previous xref
674
+			$xref = $this->getXrefData($prevxref, $xref);
675
+		}
676
+		return $xref;
677
+	}
678
+
679
+	/**
680
+	 * Get raw stream data
681
+	 * @param $offset (int) Stream offset.
682
+	 * @param $length (int) Stream length.
683
+	 * @return string Steam content
684
+	 * @protected
685
+	 */
686
+	protected function getRawStream($offset, $length) {
687
+		$offset += strspn($this->pdfdata, "\x00\x09\x0a\x0c\x0d\x20", $offset);
688
+		$offset += 6; // "stream"
689
+		$offset += strspn($this->pdfdata, "\x20", $offset);
690
+		$offset += strspn($this->pdfdata, "\r\n", $offset);
691
+
692
+		$obj = array();
693
+		$obj[] = PDF_TYPE_STREAM;
694
+		$obj[] = substr($this->pdfdata, $offset, $length);
695
+
696
+		return array($obj, $offset+$length);
697
+	}
698
+
699
+	/**
700
+	 * Get object type, raw value and offset to next object
701
+	 * @param $offset (int) Object offset.
702
+	 * @return array containing object type, raw value and offset to next object
703
+	 * @protected
704
+	 * @since 1.0.000 (2011-06-20)
705
+	 */
706
+	protected function getRawObject($offset=0, $data=null) {
707
+		if ($data == null) {
708
+			$data =& $this->pdfdata;
709
+		}
710
+		$objtype = ''; // object type to be returned
711
+		$objval = ''; // object value to be returned
712
+		// skip initial white space chars: \x00 null (NUL), \x09 horizontal tab (HT), \x0A line feed (LF), \x0C form feed (FF), \x0D carriage return (CR), \x20 space (SP)
713 713
 		while (strspn($data[$offset], "\x00\x09\x0a\x0c\x0d\x20") == 1) {
714
-            $offset++;
715
-        }
716
-        // get first char
714
+			$offset++;
715
+		}
716
+		// get first char
717 717
 		$char = $data[$offset];
718
-        // get object type
719
-        switch ($char) {
720
-            case '%': { // \x25 PERCENT SIGN
721
-                // skip comment and search for next token
722
-                $next = strcspn($data, "\r\n", $offset);
723
-                if ($next > 0) {
724
-                    $offset += $next;
725
-                    list($obj, $unused) = $this->getRawObject($offset, $data);
726
-                    return $obj;
727
-                }
728
-                break;
729
-            }
730
-            case '/': { // \x2F SOLIDUS
731
-                // name object
732
-                $objtype = PDF_TYPE_TOKEN;
733
-                ++$offset;
734
-                $length = strcspn($data, "\x00\x09\x0a\x0c\x0d\x20\x28\x29\x3c\x3e\x5b\x5d\x7b\x7d\x2f\x25", $offset);
735
-                $objval = substr($data, $offset, $length);
736
-                $offset += $length;
737
-                break;
738
-            }
739
-            case '(':   // \x28 LEFT PARENTHESIS
740
-            case ')': { // \x29 RIGHT PARENTHESIS
741
-                // literal string object
742
-                $objtype = PDF_TYPE_STRING;
743
-                ++$offset;
744
-                $strpos = $offset;
745
-                if ($char == '(') {
746
-                    $open_bracket = 1;
747
-                    while ($open_bracket > 0) {
718
+		// get object type
719
+		switch ($char) {
720
+			case '%': { // \x25 PERCENT SIGN
721
+				// skip comment and search for next token
722
+				$next = strcspn($data, "\r\n", $offset);
723
+				if ($next > 0) {
724
+					$offset += $next;
725
+					list($obj, $unused) = $this->getRawObject($offset, $data);
726
+					return $obj;
727
+				}
728
+				break;
729
+			}
730
+			case '/': { // \x2F SOLIDUS
731
+				// name object
732
+				$objtype = PDF_TYPE_TOKEN;
733
+				++$offset;
734
+				$length = strcspn($data, "\x00\x09\x0a\x0c\x0d\x20\x28\x29\x3c\x3e\x5b\x5d\x7b\x7d\x2f\x25", $offset);
735
+				$objval = substr($data, $offset, $length);
736
+				$offset += $length;
737
+				break;
738
+			}
739
+			case '(':   // \x28 LEFT PARENTHESIS
740
+			case ')': { // \x29 RIGHT PARENTHESIS
741
+				// literal string object
742
+				$objtype = PDF_TYPE_STRING;
743
+				++$offset;
744
+				$strpos = $offset;
745
+				if ($char == '(') {
746
+					$open_bracket = 1;
747
+					while ($open_bracket > 0) {
748 748
 						if (!isset($data[$strpos])) {
749
-                            break;
750
-                        }
749
+							break;
750
+						}
751 751
 						$ch = $data[$strpos];
752
-                        switch ($ch) {
753
-                            case '\\': { // REVERSE SOLIDUS (5Ch) (Backslash)
754
-                                // skip next character
755
-                                ++$strpos;
756
-                                break;
757
-                            }
758
-                            case '(': { // LEFT PARENHESIS (28h)
759
-                                ++$open_bracket;
760
-                                break;
761
-                            }
762
-                            case ')': { // RIGHT PARENTHESIS (29h)
763
-                                --$open_bracket;
764
-                                break;
765
-                            }
766
-                        }
767
-                        ++$strpos;
768
-                    }
769
-                    $objval = substr($data, $offset, ($strpos - $offset - 1));
770
-                    $offset = $strpos;
771
-                }
772
-                break;
773
-            }
774
-            case '[':   // \x5B LEFT SQUARE BRACKET
775
-            case ']': { // \x5D RIGHT SQUARE BRACKET
776
-                // array object
777
-                $objtype = PDF_TYPE_ARRAY;
778
-                ++$offset;
779
-                if ($char == '[') {
780
-                    // get array content
781
-                    $objval = array();
782
-                    do {
783
-                        // get element
784
-                        list($element, $offset) = $this->getRawObject($offset, $data);
785
-                        $objval[] = $element;
786
-                    } while ($element[0] !== ']');
787
-                    // remove closing delimiter
788
-                    array_pop($objval);
789
-                } else {
790
-                    $objtype = ']';
791
-                }
792
-                break;
793
-            }
794
-            case '<':   // \x3C LESS-THAN SIGN
795
-            case '>': { // \x3E GREATER-THAN SIGN
752
+						switch ($ch) {
753
+							case '\\': { // REVERSE SOLIDUS (5Ch) (Backslash)
754
+								// skip next character
755
+								++$strpos;
756
+								break;
757
+							}
758
+							case '(': { // LEFT PARENHESIS (28h)
759
+								++$open_bracket;
760
+								break;
761
+							}
762
+							case ')': { // RIGHT PARENTHESIS (29h)
763
+								--$open_bracket;
764
+								break;
765
+							}
766
+						}
767
+						++$strpos;
768
+					}
769
+					$objval = substr($data, $offset, ($strpos - $offset - 1));
770
+					$offset = $strpos;
771
+				}
772
+				break;
773
+			}
774
+			case '[':   // \x5B LEFT SQUARE BRACKET
775
+			case ']': { // \x5D RIGHT SQUARE BRACKET
776
+				// array object
777
+				$objtype = PDF_TYPE_ARRAY;
778
+				++$offset;
779
+				if ($char == '[') {
780
+					// get array content
781
+					$objval = array();
782
+					do {
783
+						// get element
784
+						list($element, $offset) = $this->getRawObject($offset, $data);
785
+						$objval[] = $element;
786
+					} while ($element[0] !== ']');
787
+					// remove closing delimiter
788
+					array_pop($objval);
789
+				} else {
790
+					$objtype = ']';
791
+				}
792
+				break;
793
+			}
794
+			case '<':   // \x3C LESS-THAN SIGN
795
+			case '>': { // \x3E GREATER-THAN SIGN
796 796
 				if (isset($data[($offset + 1)]) AND ($data[($offset + 1)] == $char)) {
797
-                    // dictionary object
798
-                    $objtype = PDF_TYPE_DICTIONARY;
799
-                    if ($char == '<') {
800
-                        list ($objval, $offset) = $this->getDictValue($offset, $data);
801
-                    } else {
802
-                        $objtype = '>>';
803
-                        $offset += 2;
804
-                    }
805
-                } else {
806
-                    // hexadecimal string object
807
-                    $objtype = PDF_TYPE_HEX;
808
-                    ++$offset;
809
-                    // The "Panose" entry in the FontDescriptor Style dict seems to have hex bytes separated by spaces.
810
-                    if (($char == '<') AND (preg_match('/^([0-9A-Fa-f ]+)[>]/iU', substr($data, $offset), $matches) == 1)) {
811
-                        $objval = $matches[1];
812
-                        $offset += strlen($matches[0]);
813
-                        unset($matches);
814
-                    }
815
-                }
816
-                break;
817
-            }
818
-            default: {
797
+					// dictionary object
798
+					$objtype = PDF_TYPE_DICTIONARY;
799
+					if ($char == '<') {
800
+						list ($objval, $offset) = $this->getDictValue($offset, $data);
801
+					} else {
802
+						$objtype = '>>';
803
+						$offset += 2;
804
+					}
805
+				} else {
806
+					// hexadecimal string object
807
+					$objtype = PDF_TYPE_HEX;
808
+					++$offset;
809
+					// The "Panose" entry in the FontDescriptor Style dict seems to have hex bytes separated by spaces.
810
+					if (($char == '<') AND (preg_match('/^([0-9A-Fa-f ]+)[>]/iU', substr($data, $offset), $matches) == 1)) {
811
+						$objval = $matches[1];
812
+						$offset += strlen($matches[0]);
813
+						unset($matches);
814
+					}
815
+				}
816
+				break;
817
+			}
818
+			default: {
819 819
 				$frag = $data[$offset] . @$data[$offset+1] . @$data[$offset+2] . @$data[$offset+3];
820
-                switch ($frag) {
821
-                    case 'endo':
822
-                        // indirect object
823
-                        $objtype = 'endobj';
824
-                        $offset += 6;
825
-                        break;
826
-                    case 'stre':
827
-                        // Streams should always be indirect objects, and thus processed by getRawStream().
828
-                        // If we get here, treat it as a null object as something has gone wrong.
829
-                    case 'null':
830
-                        // null object
831
-                        $objtype = PDF_TYPE_NULL;
832
-                        $offset += 4;
833
-                        $objval = 'null';
834
-                        break;
835
-                    case 'true':
836
-                        // boolean true object
837
-                        $objtype = PDF_TYPE_BOOLEAN;
838
-                        $offset += 4;
839
-                        $objval = true;
840
-                        break;
841
-                    case 'fals':
842
-                        // boolean false object
843
-                        $objtype = PDF_TYPE_BOOLEAN;
844
-                        $offset += 5;
845
-                        $objval = false;
846
-                        break;
847
-                    case 'ends':
848
-                        // end stream object
849
-                        $objtype = 'endstream';
850
-                        $offset += 9;
851
-                        break;
852
-                    default:
853
-                        if (preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+([Robj]{1,3})/i', substr($data, $offset, 33), $matches) == 1) {
854
-                            if ($matches[3] == 'R') {
855
-                                // indirect object reference
856
-                                $objtype = PDF_TYPE_OBJREF;
857
-                                $offset += strlen($matches[0]);
858
-                                $objval = array(intval($matches[1]), intval($matches[2]));
859
-                            } elseif ($matches[3] == 'obj') {
860
-                                // object start
861
-                                $objtype = PDF_TYPE_OBJECT;
862
-                                $objval = intval($matches[1]).'_'.intval($matches[2]);
863
-                                $offset += strlen ($matches[0]);
864
-                            }
865
-                        } elseif (($numlen = strspn($data, '+-.0123456789', $offset)) > 0) {
866
-                            // numeric object
867
-                            $objval = substr($data, $offset, $numlen);
868
-                            $objtype = (intval($objval) != $objval) ? PDF_TYPE_REAL : PDF_TYPE_NUMERIC;
869
-                            $offset += $numlen;
870
-                        }
871
-                        unset($matches);
872
-                        break;
873
-                }
874
-                break;
875
-            }
876
-        }
877
-        $obj = array();
878
-        $obj[] = $objtype;
879
-        if ($objtype == PDF_TYPE_OBJREF && is_array($objval)) {
880
-            foreach ($objval as $val) {
881
-                $obj[] = $val;
882
-            }
883
-        } else {
884
-            $obj[] = $objval;
885
-        }
886
-        return array($obj, $offset);
887
-    }
888
-    private function getDictValue($offset, &$data) {
889
-        $objval = array();
890
-
891
-        // Extract dict from data.
892
-        $i=1;
893
-        $dict = '';
894
-        $offset += 2;
895
-        do {
820
+				switch ($frag) {
821
+					case 'endo':
822
+						// indirect object
823
+						$objtype = 'endobj';
824
+						$offset += 6;
825
+						break;
826
+					case 'stre':
827
+						// Streams should always be indirect objects, and thus processed by getRawStream().
828
+						// If we get here, treat it as a null object as something has gone wrong.
829
+					case 'null':
830
+						// null object
831
+						$objtype = PDF_TYPE_NULL;
832
+						$offset += 4;
833
+						$objval = 'null';
834
+						break;
835
+					case 'true':
836
+						// boolean true object
837
+						$objtype = PDF_TYPE_BOOLEAN;
838
+						$offset += 4;
839
+						$objval = true;
840
+						break;
841
+					case 'fals':
842
+						// boolean false object
843
+						$objtype = PDF_TYPE_BOOLEAN;
844
+						$offset += 5;
845
+						$objval = false;
846
+						break;
847
+					case 'ends':
848
+						// end stream object
849
+						$objtype = 'endstream';
850
+						$offset += 9;
851
+						break;
852
+					default:
853
+						if (preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+([Robj]{1,3})/i', substr($data, $offset, 33), $matches) == 1) {
854
+							if ($matches[3] == 'R') {
855
+								// indirect object reference
856
+								$objtype = PDF_TYPE_OBJREF;
857
+								$offset += strlen($matches[0]);
858
+								$objval = array(intval($matches[1]), intval($matches[2]));
859
+							} elseif ($matches[3] == 'obj') {
860
+								// object start
861
+								$objtype = PDF_TYPE_OBJECT;
862
+								$objval = intval($matches[1]).'_'.intval($matches[2]);
863
+								$offset += strlen ($matches[0]);
864
+							}
865
+						} elseif (($numlen = strspn($data, '+-.0123456789', $offset)) > 0) {
866
+							// numeric object
867
+							$objval = substr($data, $offset, $numlen);
868
+							$objtype = (intval($objval) != $objval) ? PDF_TYPE_REAL : PDF_TYPE_NUMERIC;
869
+							$offset += $numlen;
870
+						}
871
+						unset($matches);
872
+						break;
873
+				}
874
+				break;
875
+			}
876
+		}
877
+		$obj = array();
878
+		$obj[] = $objtype;
879
+		if ($objtype == PDF_TYPE_OBJREF && is_array($objval)) {
880
+			foreach ($objval as $val) {
881
+				$obj[] = $val;
882
+			}
883
+		} else {
884
+			$obj[] = $objval;
885
+		}
886
+		return array($obj, $offset);
887
+	}
888
+	private function getDictValue($offset, &$data) {
889
+		$objval = array();
890
+
891
+		// Extract dict from data.
892
+		$i=1;
893
+		$dict = '';
894
+		$offset += 2;
895
+		do {
896 896
 			if ($data[$offset] == '>' && $data[$offset+1] == '>') {
897
-                $i--;
898
-                $dict .= '>>';
899
-                $offset += 2;
897
+				$i--;
898
+				$dict .= '>>';
899
+				$offset += 2;
900 900
 			} else if ($data[$offset] == '<' && $data[$offset+1] == '<') {
901
-                $i++;
902
-                $dict .= '<<';
903
-                $offset += 2;
904
-            } else {
901
+				$i++;
902
+				$dict .= '<<';
903
+				$offset += 2;
904
+			} else {
905 905
 				$dict .= $data[$offset];
906
-                $offset++;
907
-            }
908
-        } while ($i>0);
909
-
910
-        // Now that we have just the dict, parse it.
911
-        $dictoffset = 0;
912
-        do {
913
-            // Get dict element.
914
-            list($key, $eloffset) = $this->getRawObject($dictoffset, $dict);
915
-            if ($key[0] == '>>') {
916
-                break;
917
-            }
918
-            list($element, $dictoffset) = $this->getRawObject($eloffset, $dict);
919
-            $objval['/'.$key[1]] = $element;
920
-            unset($key);
921
-            unset($element);
922
-        } while (true);
923
-
924
-        return array($objval, $offset);
925
-    }
926
-
927
-    /**
928
-     * Get content of indirect object.
929
-     * @param $obj_ref (string) Object number and generation number separated by underscore character.
930
-     * @param $offset (int) Object offset.
931
-     * @param $decoding (boolean) If true decode streams.
932
-     * @return array containing object data.
933
-     * @protected
934
-     * @since 1.0.000 (2011-05-24)
935
-     */
936
-    protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) {
937
-        $obj = explode('_', $obj_ref);
938
-        if (($obj === false) OR (count($obj) != 2)) {
939
-            $this->Error('Invalid object reference: '.$obj);
940
-            return;
941
-        }
942
-        $objref = $obj[0].' '.$obj[1].' obj';
943
-
944
-        if (strpos($this->pdfdata, $objref, $offset) != $offset) {
945
-            // an indirect reference to an undefined object shall be considered a reference to the null object
946
-            return array('null', 'null', $offset);
947
-        }
948
-        // starting position of object content
949
-        $offset += strlen($objref);
950
-        // get array of object content
951
-        $objdata = array();
952
-        $i = 0; // object main index
953
-        do {
954
-            if (($i > 0) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY) AND array_key_exists('/Length', $objdata[($i - 1)][1])) {
955
-                // Stream - get using /Length in stream's dict
956
-                $lengthobj = $objdata[($i-1)][1]['/Length'];
957
-                if ($lengthobj[0] === PDF_TYPE_OBJREF) {
958
-                    $lengthobj = $this->getObjectVal($lengthobj);
959
-                    if ($lengthobj[0] === PDF_TYPE_OBJECT) {
960
-                        $lengthobj = $lengthobj[1];
961
-                    }
962
-                }
963
-                $streamlength = $lengthobj[1];
964
-                list($element, $offset) = $this->getRawStream($offset, $streamlength);
965
-            } else {
966
-                // get element
967
-                list($element, $offset) = $this->getRawObject($offset);
968
-            }
969
-            // decode stream using stream's dictionary information
970
-            if ($decoding AND ($element[0] == PDF_TYPE_STREAM) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY)) {
971
-                $element[3] = $this->decodeStream($objdata[($i - 1)][1], $element[1]);
972
-            }
973
-            $objdata[$i] = $element;
974
-            ++$i;
975
-        } while ($element[0] != 'endobj');
976
-        // remove closing delimiter
977
-        array_pop($objdata);
978
-        // return raw object content
979
-        return $objdata;
980
-    }
981
-
982
-    /**
983
-     * Get the content of object, resolving indect object reference if necessary.
984
-     * @param $obj (string) Object value.
985
-     * @return array containing object data.
986
-     * @public
987
-     * @since 1.0.000 (2011-06-26)
988
-     */
989
-    public function getObjectVal($obj) {
990
-        if ($obj[0] == PDF_TYPE_OBJREF) {
991
-            if (strpos($obj[1], '_') !== false) {
992
-                $key = explode('_', $obj[1]);
993
-            } else {
994
-                $key = array($obj[1], $obj[2]);
995
-            }
996
-
997
-            $ret = array(0=>PDF_TYPE_OBJECT, 'obj'=>$key[0], 'gen'=>$key[1]);
998
-
999
-            // reference to indirect object
1000
-            $object = null;
1001
-            if (isset($this->objects[$key[0]][$key[1]])) {
1002
-                // this object has been already parsed
1003
-                $object = $this->objects[$key[0]][$key[1]];
1004
-            } elseif (($offset = $this->findObjectOffset($key)) !== false) {
1005
-                // parse new object
1006
-                $this->objects[$key[0]][$key[1]] = $this->getIndirectObject($key[0].'_'.$key[1], $offset, false);
1007
-                $object = $this->objects[$key[0]][$key[1]];
1008
-            } elseif (($key[1] == 0) && isset($this->objstreamobjs[$key[0]])) {
1009
-                // Object is in an object stream
1010
-                $streaminfo = $this->objstreamobjs[$key[0]];
1011
-                $objs = $streaminfo[0];
1012
-                if (!isset($this->objstreams[$objs[0]][$objs[1]])) {
1013
-                    // Fetch and decode object stream
1014
-                    $offset = $this->findObjectOffset($objs);;
1015
-                    $objstream = $this->getObjectVal(array(PDF_TYPE_OBJREF, $objs[0], $objs[1]));
1016
-                    $decoded = $this->decodeStream($objstream[1][1], $objstream[2][1]);
1017
-                    $this->objstreams[$objs[0]][$objs[1]] = $decoded[0]; // Store just the data, in case we need more from this objstream
1018
-                    // Free memory
1019
-                    unset($objstream);
1020
-                    unset($decoded);
1021
-                }
1022
-                $this->objects[$key[0]][$key[1]] = $this->getRawObject($streaminfo[1], $this->objstreams[$objs[0]][$objs[1]]);
1023
-                $object = $this->objects[$key[0]][$key[1]];
1024
-            }
1025
-            if (!is_null($object)) {
1026
-                $ret[1] = $object[0];
1027
-                if (isset($object[1][0]) && $object[1][0] == PDF_TYPE_STREAM) {
1028
-                    $ret[0] = PDF_TYPE_STREAM;
1029
-                    $ret[2] = $object[1];
1030
-                }
1031
-                return $ret;
1032
-            }
1033
-        }
1034
-        return $obj;
1035
-    }
1036
-
1037
-    /**
1038
-     * Extract object stream to find out what it contains.
1039
-     *
1040
-     */
1041
-    function extractObjectStream($key) {
1042
-        $objref = array(PDF_TYPE_OBJREF, $key[0], $key[1]);
1043
-        $obj = $this->getObjectVal($objref);
1044
-        if ($obj[0] !== PDF_TYPE_STREAM || !isset($obj[1][1]['/First'][1])) {
1045
-            // Not a valid object stream dictionary - skip it.
1046
-            return;
1047
-        }
1048
-        $stream = $this->decodeStream($obj[1][1], $obj[2][1]);// Decode object stream, as we need the first bit
1049
-        $first = intval($obj[1][1]['/First'][1]);
1050
-        $ints = preg_split('/\s/', substr($stream[0], 0, $first)); // Get list of object / offset pairs
1051
-        for ($j=1; $j<count($ints); $j++) {
1052
-            if (($j % 2) == 1) {
1053
-                $this->objstreamobjs[$ints[$j-1]] = array($key, $ints[$j]+$first);
1054
-            }
1055
-        }
1056
-
1057
-        // Free memory - we may not need this at all.
1058
-        unset($obj);
1059
-        unset($stream);
1060
-    }
1061
-
1062
-    /**
1063
-     * Find all object offsets.  Saves having to scour the file multiple times.
1064
-     * @private
1065
-     */
1066
-    private function findObjectOffsets() {
1067
-        $this->objoffsets = array();
1068
-        if (preg_match_all('/(*ANYCRLF)^[\s]*([0-9]+)[\s]+([0-9]+)[\s]+obj/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE) >= 1) {
1069
-            $i = 0;
1070
-            $laststreamend = 0;
1071
-            foreach($matches[0] as $match) {
1072
-                $offset = $match[1] + strspn($match[0], "\x00\x09\x0a\x0c\x0d\x20");
1073
-                if ($offset < $laststreamend) {
1074
-                    // Contained within another stream, skip it.
1075
-                    continue;
1076
-                }
1077
-                $this->objoffsets[trim($match[0])] = $offset;
1078
-                $dictoffset = $match[1] + strlen($match[0]);
1079
-                $dictfrag = substr($this->pdfdata, $dictoffset, 256);
1080
-                if (preg_match('|^\s+<<[^>]+/Length\s+(\d+)|', $dictfrag, $lengthmatch, PREG_OFFSET_CAPTURE) == 1) {
1081
-                    $laststreamend += intval($lengthmatch[1][0]);
1082
-                }
1083
-                if (preg_match('|^\s+<<[^>]+/ObjStm|', $dictfrag, $objstm) == 1) {
1084
-                    $this->extractObjectStream(array($matches[1][$i][0], $matches[2][$i][0]));
1085
-                }
1086
-                $i++;
1087
-            }
1088
-        }
1089
-        unset($lengthmatch);
1090
-        unset($dictfrag);
1091
-        unset($matches);
1092
-    }
1093
-
1094
-    /**
1095
-     * Get offset of an object.  Checks xref first, then offsets found by scouring the file.
1096
-     * @param $key (array) Object key to find (obj, gen).
1097
-     * @return int Offset of the object in $this->pdfdata.
1098
-     * @private
1099
-     */
1100
-    private function findObjectOffset($key) {
1101
-        $objref = $key[0].' '.$key[1].' obj';
1102
-        if (isset($this->xref['xref'][$key[0]][$key[1]])) {
1103
-            $offset = $this->xref['xref'][$key[0]][$key[1]];
1104
-            if (strpos($this->pdfdata, $objref, $offset) === $offset) {
1105
-                // Offset is in xref table and matches actual position in file
1106
-                //echo "Offset in XREF is correct, returning<br>";
1107
-                return $this->xref['xref'][$key[0]][$key[1]];
1108
-            }
1109
-        }
1110
-        if (array_key_exists($objref, $this->objoffsets)) {
1111
-            //echo "Offset found in internal reftable<br>";
1112
-            return $this->objoffsets[$objref];
1113
-        }
1114
-        return false;
1115
-    }
1116
-
1117
-    /**
1118
-     * Decode the specified stream.
1119
-     * @param $sdic (array) Stream's dictionary array.
1120
-     * @param $stream (string) Stream to decode.
1121
-     * @return array containing decoded stream data and remaining filters.
1122
-     * @protected
1123
-     * @since 1.0.000 (2011-06-22)
1124
-     */
1125
-    protected function decodeStream($sdic, $stream) {
1126
-        // get stream lenght and filters
1127
-        $slength = strlen($stream);
1128
-        if ($slength <= 0) {
1129
-            return array('', array());
1130
-        }
1131
-        $filters = array();
1132
-        foreach ($sdic as $k => $v) {
1133
-            if ($v[0] == PDF_TYPE_TOKEN) {
1134
-                if (($k == '/Length') AND ($v[0] == PDF_TYPE_NUMERIC)) {
1135
-                    // get declared stream lenght
1136
-                    $declength = intval($v[1]);
1137
-                    if ($declength < $slength) {
1138
-                        $stream = substr($stream, 0, $declength);
1139
-                        $slength = $declength;
1140
-                    }
1141
-                } elseif ($k == '/Filter') {
1142
-                    if ($v[0] == PDF_TYPE_TOKEN) {
1143
-                        // single filter
1144
-                        $filters[] = $v[1];
1145
-                    } elseif ($v[0] == PDF_TYPE_ARRAY) {
1146
-                        // array of filters
1147
-                        foreach ($v[1] as $flt) {
1148
-                            if ($flt[0] == PDF_TYPE_TOKEN) {
1149
-                                $filters[] = $flt[1];
1150
-                            }
1151
-                        }
1152
-                    }
1153
-                }
1154
-            }
1155
-        }
1156
-        // decode the stream
1157
-        $remaining_filters = array();
1158
-        foreach ($filters as $filter) {
1159
-            if (in_array($filter, $this->FilterDecoders->getAvailableFilters())) {
1160
-                $stream = $this->FilterDecoders->decodeFilter($filter, $stream);
1161
-            } else {
1162
-                // add missing filter to array
1163
-                $remaining_filters[] = $filter;
1164
-            }
1165
-        }
1166
-        return array($stream, $remaining_filters);
1167
-    }
1168
-
1169
-
1170
-    /**
1171
-     * Set pageno
1172
-     *
1173
-     * @param int $pageno Pagenumber to use
1174
-     */
1175
-    public function setPageno($pageno) {
1176
-        $pageno = ((int) $pageno) - 1;
1177
-
1178
-        if ($pageno < 0 || $pageno >= $this->getPageCount()) {
1179
-            $this->error("Pagenumber is wrong! (Requested $pageno, max ".$this->getPageCount().")");
1180
-        }
1181
-
1182
-        $this->pageno = $pageno;
1183
-    }
1184
-
1185
-    /**
1186
-     * Get page-resources from current page
1187
-     *
1188
-     * @return array
1189
-     */
1190
-    public function getPageResources() {
1191
-        return $this->_getPageResources($this->pages[$this->pageno]);
1192
-    }
1193
-
1194
-    /**
1195
-     * Get page-resources from /Page
1196
-     *
1197
-     * @param array $obj Array of pdf-data
1198
-     */
1199
-    private function _getPageResources ($obj) { // $obj = /Page
1200
-        $obj = $this->getObjectVal($obj);
1201
-
1202
-        // If the current object has a resources
1203
-        // dictionary associated with it, we use
1204
-        // it. Otherwise, we move back to its
1205
-        // parent object.
1206
-        if (isset ($obj[1][1]['/Resources'])) {
1207
-            $res = $obj[1][1]['/Resources'];
1208
-            if ($res[0] == PDF_TYPE_OBJECT)
1209
-                return $res[1];
1210
-            return $res;
1211
-        } else {
1212
-            if (!isset ($obj[1][1]['/Parent'])) {
1213
-                return false;
1214
-            } else {
1215
-                $res = $this->_getPageResources($obj[1][1]['/Parent']);
1216
-                if ($res[0] == PDF_TYPE_OBJECT)
1217
-                    return $res[1];
1218
-                return $res;
1219
-            }
1220
-        }
1221
-    }
1222
-
1223
-    /**
1224
-     * Get annotations from current page
1225
-     *
1226
-     * @return array
1227
-     */
1228
-    public function getPageAnnotations() {
1229
-        return $this->_getPageAnnotations($this->pages[$this->pageno]);
1230
-    }
1231
-
1232
-    /**
1233
-     * Get annotations from /Page
1234
-     *
1235
-     * @param array $obj Array of pdf-data
1236
-     */
1237
-    private function _getPageAnnotations ($obj) { // $obj = /Page
1238
-        $obj = $this->getObjectVal($obj);
1239
-
1240
-        // If the current object has an annotations
1241
-        // dictionary associated with it, we use
1242
-        // it. Otherwise, we move back to its
1243
-        // parent object.
1244
-        if (isset ($obj[1][1]['/Annots'])) {
1245
-            $annots = $obj[1][1]['/Annots'];
1246
-        } else {
1247
-            if (!isset ($obj[1][1]['/Parent'])) {
1248
-                return false;
1249
-            } else {
1250
-                $annots = $this->_getPageAnnotations($obj[1][1]['/Parent']);
1251
-            }
1252
-        }
1253
-
1254
-        if ($annots[0] == PDF_TYPE_OBJREF)
1255
-            return $this->getObjectVal($annots);
1256
-        return $annots;
1257
-    }
1258
-
1259
-
1260
-    /**
1261
-     * Get content of current page
1262
-     *
1263
-     * If more /Contents is an array, the streams are concated
1264
-     *
1265
-     * @return string
1266
-     */
1267
-    public function getContent() {
1268
-        $buffer = '';
1269
-
1270
-        if (isset($this->pages[$this->pageno][1][1]['/Contents'])) {
1271
-            $contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']);
1272
-            foreach($contents AS $tmp_content) {
1273
-                $buffer .= $this->_rebuildContentStream($tmp_content) . ' ';
1274
-            }
1275
-        }
1276
-
1277
-        return $buffer;
1278
-    }
1279
-
1280
-
1281
-    /**
1282
-     * Resolve all content-objects
1283
-     *
1284
-     * @param array $content_ref
1285
-     * @return array
1286
-     */
1287
-    private function _getPageContent($content_ref) {
1288
-        $contents = array();
1289
-
1290
-        if ($content_ref[0] == PDF_TYPE_OBJREF) {
1291
-            $content = $this->getObjectVal($content_ref);
1292
-            if ($content[1][0] == PDF_TYPE_ARRAY) {
1293
-                $contents = $this->_getPageContent($content[1]);
1294
-            } else {
1295
-                $contents[] = $content;
1296
-            }
1297
-        } elseif ($content_ref[0] == PDF_TYPE_ARRAY) {
1298
-            foreach ($content_ref[1] AS $tmp_content_ref) {
1299
-                $contents = array_merge($contents,$this->_getPageContent($tmp_content_ref));
1300
-            }
1301
-        }
1302
-
1303
-        return $contents;
1304
-    }
1305
-
1306
-
1307
-    /**
1308
-     * Rebuild content-streams
1309
-     *
1310
-     * @param array $obj
1311
-     * @return string
1312
-     */
1313
-    private function _rebuildContentStream($obj) {
1314
-        $filters = array();
1315
-
1316
-        if (isset($obj[1][1]['/Filter'])) {
1317
-            $_filter = $obj[1][1]['/Filter'];
1318
-
1319
-            if ($_filter[0] == PDF_TYPE_OBJREF) {
1320
-                $tmpFilter = $this->getObjectVal($_filter);
1321
-                $_filter = $tmpFilter[1];
1322
-            }
1323
-
1324
-            if ($_filter[0] == PDF_TYPE_TOKEN) {
1325
-                $filters[] = $_filter;
1326
-            } elseif ($_filter[0] == PDF_TYPE_ARRAY) {
1327
-                $filters = $_filter[1];
1328
-            }
1329
-        }
1330
-
1331
-        $stream = $obj[2][1];
1332
-
1333
-        foreach ($filters AS $_filter) {
1334
-            $stream = $this->FilterDecoders->decodeFilter($_filter[1], $stream);
1335
-        }
1336
-
1337
-        return $stream;
1338
-    }
1339
-
1340
-
1341
-    /**
1342
-     * Get a Box from a page
1343
-     * Arrayformat is same as used by fpdf_tpl
1344
-     *
1345
-     * @param array $page a /Page
1346
-     * @param string $box_index Type of Box @see $availableBoxes
1347
-     * @param float Scale factor from user space units to points
1348
-     * @return array
1349
-     */
1350
-    public function getPageBox($page, $box_index, $k) {
1351
-        $page = $this->getObjectVal($page);
1352
-        $box = null;
1353
-        if (isset($page[1][1][$box_index]))
1354
-            $box =& $page[1][1][$box_index];
1355
-
1356
-        if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) {
1357
-            $tmp_box = $this->getObjectVal($box);
1358
-            $box = $tmp_box[1];
1359
-        }
1360
-
1361
-        if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) {
1362
-            $b =& $box[1];
1363
-            return array('x' => $b[0][1] / $k,
1364
-                         'y' => $b[1][1] / $k,
1365
-                         'w' => abs($b[0][1] - $b[2][1]) / $k,
1366
-                         'h' => abs($b[1][1] - $b[3][1]) / $k,
1367
-                         'llx' => min($b[0][1], $b[2][1]) / $k,
1368
-                         'lly' => min($b[1][1], $b[3][1]) / $k,
1369
-                         'urx' => max($b[0][1], $b[2][1]) / $k,
1370
-                         'ury' => max($b[1][1], $b[3][1]) / $k,
1371
-                         );
1372
-        } elseif (!isset ($page[1][1]['/Parent'])) {
1373
-            return false;
1374
-        } else {
1375
-            return $this->getPageBox($this->getObjectVal($page[1][1]['/Parent']), $box_index, $k);
1376
-        }
1377
-    }
1378
-
1379
-    /**
1380
-     * Get all page boxes by page no
1381
-     *
1382
-     * @param int The page number
1383
-     * @param float Scale factor from user space units to points
1384
-     * @return array
1385
-     */
1386
-    public function getPageBoxes($pageno, $k) {
1387
-        return $this->_getPageBoxes($this->pages[$pageno - 1], $k);
1388
-    }
1389
-
1390
-    /**
1391
-     * Get all boxes from /Page
1392
-     *
1393
-     * @param array a /Page
1394
-     * @return array
1395
-     */
1396
-    private function _getPageBoxes($page, $k) {
1397
-        $boxes = array();
1398
-
1399
-        foreach($this->availableBoxes AS $box) {
1400
-            if ($_box = $this->getPageBox($page, $box, $k)) {
1401
-                $boxes[$box] = $_box;
1402
-            }
1403
-        }
1404
-
1405
-        return $boxes;
1406
-    }
1407
-
1408
-    /**
1409
-     * Get the page rotation by pageno
1410
-     *
1411
-     * @param integer $pageno
1412
-     * @return array
1413
-     */
1414
-    public function getPageRotation($pageno) {
1415
-        return $this->_getPageRotation($this->pages[$pageno - 1]);
1416
-    }
1417
-
1418
-    private function _getPageRotation($obj) { // $obj = /Page
1419
-        $obj = $this->getObjectVal($obj);
1420
-        if (isset ($obj[1][1]['/Rotate'])) {
1421
-            $res = $this->getObjectVal($obj[1][1]['/Rotate']);
1422
-    		if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
1423
-                return $res[1];
1424
-            return $res;
1425
-        } else {
1426
-            if (!isset ($obj[1][1]['/Parent'])) {
1427
-                return false;
1428
-            } else {
1429
-                $res = $this->_getPageRotation($obj[1][1]['/Parent']);
1430
-                if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
1431
-                    return $res[1];
1432
-                return $res;
1433
-            }
1434
-        }
1435
-    }
1436
-
1437
-    /**
1438
-     * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution.
1439
-     * @param $msg (string) The error message
1440
-     * @public
1441
-     * @since 1.0.000 (2011-05-23)
1442
-     */
1443
-    public function Error($msg) {
1444
-        // exit program and print error
1445
-        die("<strong>TCPDI_PARSER ERROR [{$this->uniqueid}]: </strong>".$msg);
1446
-    }
906
+				$offset++;
907
+			}
908
+		} while ($i>0);
909
+
910
+		// Now that we have just the dict, parse it.
911
+		$dictoffset = 0;
912
+		do {
913
+			// Get dict element.
914
+			list($key, $eloffset) = $this->getRawObject($dictoffset, $dict);
915
+			if ($key[0] == '>>') {
916
+				break;
917
+			}
918
+			list($element, $dictoffset) = $this->getRawObject($eloffset, $dict);
919
+			$objval['/'.$key[1]] = $element;
920
+			unset($key);
921
+			unset($element);
922
+		} while (true);
923
+
924
+		return array($objval, $offset);
925
+	}
926
+
927
+	/**
928
+	 * Get content of indirect object.
929
+	 * @param $obj_ref (string) Object number and generation number separated by underscore character.
930
+	 * @param $offset (int) Object offset.
931
+	 * @param $decoding (boolean) If true decode streams.
932
+	 * @return array containing object data.
933
+	 * @protected
934
+	 * @since 1.0.000 (2011-05-24)
935
+	 */
936
+	protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) {
937
+		$obj = explode('_', $obj_ref);
938
+		if (($obj === false) OR (count($obj) != 2)) {
939
+			$this->Error('Invalid object reference: '.$obj);
940
+			return;
941
+		}
942
+		$objref = $obj[0].' '.$obj[1].' obj';
943
+
944
+		if (strpos($this->pdfdata, $objref, $offset) != $offset) {
945
+			// an indirect reference to an undefined object shall be considered a reference to the null object
946
+			return array('null', 'null', $offset);
947
+		}
948
+		// starting position of object content
949
+		$offset += strlen($objref);
950
+		// get array of object content
951
+		$objdata = array();
952
+		$i = 0; // object main index
953
+		do {
954
+			if (($i > 0) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY) AND array_key_exists('/Length', $objdata[($i - 1)][1])) {
955
+				// Stream - get using /Length in stream's dict
956
+				$lengthobj = $objdata[($i-1)][1]['/Length'];
957
+				if ($lengthobj[0] === PDF_TYPE_OBJREF) {
958
+					$lengthobj = $this->getObjectVal($lengthobj);
959
+					if ($lengthobj[0] === PDF_TYPE_OBJECT) {
960
+						$lengthobj = $lengthobj[1];
961
+					}
962
+				}
963
+				$streamlength = $lengthobj[1];
964
+				list($element, $offset) = $this->getRawStream($offset, $streamlength);
965
+			} else {
966
+				// get element
967
+				list($element, $offset) = $this->getRawObject($offset);
968
+			}
969
+			// decode stream using stream's dictionary information
970
+			if ($decoding AND ($element[0] == PDF_TYPE_STREAM) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY)) {
971
+				$element[3] = $this->decodeStream($objdata[($i - 1)][1], $element[1]);
972
+			}
973
+			$objdata[$i] = $element;
974
+			++$i;
975
+		} while ($element[0] != 'endobj');
976
+		// remove closing delimiter
977
+		array_pop($objdata);
978
+		// return raw object content
979
+		return $objdata;
980
+	}
981
+
982
+	/**
983
+	 * Get the content of object, resolving indect object reference if necessary.
984
+	 * @param $obj (string) Object value.
985
+	 * @return array containing object data.
986
+	 * @public
987
+	 * @since 1.0.000 (2011-06-26)
988
+	 */
989
+	public function getObjectVal($obj) {
990
+		if ($obj[0] == PDF_TYPE_OBJREF) {
991
+			if (strpos($obj[1], '_') !== false) {
992
+				$key = explode('_', $obj[1]);
993
+			} else {
994
+				$key = array($obj[1], $obj[2]);
995
+			}
996
+
997
+			$ret = array(0=>PDF_TYPE_OBJECT, 'obj'=>$key[0], 'gen'=>$key[1]);
998
+
999
+			// reference to indirect object
1000
+			$object = null;
1001
+			if (isset($this->objects[$key[0]][$key[1]])) {
1002
+				// this object has been already parsed
1003
+				$object = $this->objects[$key[0]][$key[1]];
1004
+			} elseif (($offset = $this->findObjectOffset($key)) !== false) {
1005
+				// parse new object
1006
+				$this->objects[$key[0]][$key[1]] = $this->getIndirectObject($key[0].'_'.$key[1], $offset, false);
1007
+				$object = $this->objects[$key[0]][$key[1]];
1008
+			} elseif (($key[1] == 0) && isset($this->objstreamobjs[$key[0]])) {
1009
+				// Object is in an object stream
1010
+				$streaminfo = $this->objstreamobjs[$key[0]];
1011
+				$objs = $streaminfo[0];
1012
+				if (!isset($this->objstreams[$objs[0]][$objs[1]])) {
1013
+					// Fetch and decode object stream
1014
+					$offset = $this->findObjectOffset($objs);;
1015
+					$objstream = $this->getObjectVal(array(PDF_TYPE_OBJREF, $objs[0], $objs[1]));
1016
+					$decoded = $this->decodeStream($objstream[1][1], $objstream[2][1]);
1017
+					$this->objstreams[$objs[0]][$objs[1]] = $decoded[0]; // Store just the data, in case we need more from this objstream
1018
+					// Free memory
1019
+					unset($objstream);
1020
+					unset($decoded);
1021
+				}
1022
+				$this->objects[$key[0]][$key[1]] = $this->getRawObject($streaminfo[1], $this->objstreams[$objs[0]][$objs[1]]);
1023
+				$object = $this->objects[$key[0]][$key[1]];
1024
+			}
1025
+			if (!is_null($object)) {
1026
+				$ret[1] = $object[0];
1027
+				if (isset($object[1][0]) && $object[1][0] == PDF_TYPE_STREAM) {
1028
+					$ret[0] = PDF_TYPE_STREAM;
1029
+					$ret[2] = $object[1];
1030
+				}
1031
+				return $ret;
1032
+			}
1033
+		}
1034
+		return $obj;
1035
+	}
1036
+
1037
+	/**
1038
+	 * Extract object stream to find out what it contains.
1039
+	 *
1040
+	 */
1041
+	function extractObjectStream($key) {
1042
+		$objref = array(PDF_TYPE_OBJREF, $key[0], $key[1]);
1043
+		$obj = $this->getObjectVal($objref);
1044
+		if ($obj[0] !== PDF_TYPE_STREAM || !isset($obj[1][1]['/First'][1])) {
1045
+			// Not a valid object stream dictionary - skip it.
1046
+			return;
1047
+		}
1048
+		$stream = $this->decodeStream($obj[1][1], $obj[2][1]);// Decode object stream, as we need the first bit
1049
+		$first = intval($obj[1][1]['/First'][1]);
1050
+		$ints = preg_split('/\s/', substr($stream[0], 0, $first)); // Get list of object / offset pairs
1051
+		for ($j=1; $j<count($ints); $j++) {
1052
+			if (($j % 2) == 1) {
1053
+				$this->objstreamobjs[$ints[$j-1]] = array($key, $ints[$j]+$first);
1054
+			}
1055
+		}
1056
+
1057
+		// Free memory - we may not need this at all.
1058
+		unset($obj);
1059
+		unset($stream);
1060
+	}
1061
+
1062
+	/**
1063
+	 * Find all object offsets.  Saves having to scour the file multiple times.
1064
+	 * @private
1065
+	 */
1066
+	private function findObjectOffsets() {
1067
+		$this->objoffsets = array();
1068
+		if (preg_match_all('/(*ANYCRLF)^[\s]*([0-9]+)[\s]+([0-9]+)[\s]+obj/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE) >= 1) {
1069
+			$i = 0;
1070
+			$laststreamend = 0;
1071
+			foreach($matches[0] as $match) {
1072
+				$offset = $match[1] + strspn($match[0], "\x00\x09\x0a\x0c\x0d\x20");
1073
+				if ($offset < $laststreamend) {
1074
+					// Contained within another stream, skip it.
1075
+					continue;
1076
+				}
1077
+				$this->objoffsets[trim($match[0])] = $offset;
1078
+				$dictoffset = $match[1] + strlen($match[0]);
1079
+				$dictfrag = substr($this->pdfdata, $dictoffset, 256);
1080
+				if (preg_match('|^\s+<<[^>]+/Length\s+(\d+)|', $dictfrag, $lengthmatch, PREG_OFFSET_CAPTURE) == 1) {
1081
+					$laststreamend += intval($lengthmatch[1][0]);
1082
+				}
1083
+				if (preg_match('|^\s+<<[^>]+/ObjStm|', $dictfrag, $objstm) == 1) {
1084
+					$this->extractObjectStream(array($matches[1][$i][0], $matches[2][$i][0]));
1085
+				}
1086
+				$i++;
1087
+			}
1088
+		}
1089
+		unset($lengthmatch);
1090
+		unset($dictfrag);
1091
+		unset($matches);
1092
+	}
1093
+
1094
+	/**
1095
+	 * Get offset of an object.  Checks xref first, then offsets found by scouring the file.
1096
+	 * @param $key (array) Object key to find (obj, gen).
1097
+	 * @return int Offset of the object in $this->pdfdata.
1098
+	 * @private
1099
+	 */
1100
+	private function findObjectOffset($key) {
1101
+		$objref = $key[0].' '.$key[1].' obj';
1102
+		if (isset($this->xref['xref'][$key[0]][$key[1]])) {
1103
+			$offset = $this->xref['xref'][$key[0]][$key[1]];
1104
+			if (strpos($this->pdfdata, $objref, $offset) === $offset) {
1105
+				// Offset is in xref table and matches actual position in file
1106
+				//echo "Offset in XREF is correct, returning<br>";
1107
+				return $this->xref['xref'][$key[0]][$key[1]];
1108
+			}
1109
+		}
1110
+		if (array_key_exists($objref, $this->objoffsets)) {
1111
+			//echo "Offset found in internal reftable<br>";
1112
+			return $this->objoffsets[$objref];
1113
+		}
1114
+		return false;
1115
+	}
1116
+
1117
+	/**
1118
+	 * Decode the specified stream.
1119
+	 * @param $sdic (array) Stream's dictionary array.
1120
+	 * @param $stream (string) Stream to decode.
1121
+	 * @return array containing decoded stream data and remaining filters.
1122
+	 * @protected
1123
+	 * @since 1.0.000 (2011-06-22)
1124
+	 */
1125
+	protected function decodeStream($sdic, $stream) {
1126
+		// get stream lenght and filters
1127
+		$slength = strlen($stream);
1128
+		if ($slength <= 0) {
1129
+			return array('', array());
1130
+		}
1131
+		$filters = array();
1132
+		foreach ($sdic as $k => $v) {
1133
+			if ($v[0] == PDF_TYPE_TOKEN) {
1134
+				if (($k == '/Length') AND ($v[0] == PDF_TYPE_NUMERIC)) {
1135
+					// get declared stream lenght
1136
+					$declength = intval($v[1]);
1137
+					if ($declength < $slength) {
1138
+						$stream = substr($stream, 0, $declength);
1139
+						$slength = $declength;
1140
+					}
1141
+				} elseif ($k == '/Filter') {
1142
+					if ($v[0] == PDF_TYPE_TOKEN) {
1143
+						// single filter
1144
+						$filters[] = $v[1];
1145
+					} elseif ($v[0] == PDF_TYPE_ARRAY) {
1146
+						// array of filters
1147
+						foreach ($v[1] as $flt) {
1148
+							if ($flt[0] == PDF_TYPE_TOKEN) {
1149
+								$filters[] = $flt[1];
1150
+							}
1151
+						}
1152
+					}
1153
+				}
1154
+			}
1155
+		}
1156
+		// decode the stream
1157
+		$remaining_filters = array();
1158
+		foreach ($filters as $filter) {
1159
+			if (in_array($filter, $this->FilterDecoders->getAvailableFilters())) {
1160
+				$stream = $this->FilterDecoders->decodeFilter($filter, $stream);
1161
+			} else {
1162
+				// add missing filter to array
1163
+				$remaining_filters[] = $filter;
1164
+			}
1165
+		}
1166
+		return array($stream, $remaining_filters);
1167
+	}
1168
+
1169
+
1170
+	/**
1171
+	 * Set pageno
1172
+	 *
1173
+	 * @param int $pageno Pagenumber to use
1174
+	 */
1175
+	public function setPageno($pageno) {
1176
+		$pageno = ((int) $pageno) - 1;
1177
+
1178
+		if ($pageno < 0 || $pageno >= $this->getPageCount()) {
1179
+			$this->error("Pagenumber is wrong! (Requested $pageno, max ".$this->getPageCount().")");
1180
+		}
1181
+
1182
+		$this->pageno = $pageno;
1183
+	}
1184
+
1185
+	/**
1186
+	 * Get page-resources from current page
1187
+	 *
1188
+	 * @return array
1189
+	 */
1190
+	public function getPageResources() {
1191
+		return $this->_getPageResources($this->pages[$this->pageno]);
1192
+	}
1193
+
1194
+	/**
1195
+	 * Get page-resources from /Page
1196
+	 *
1197
+	 * @param array $obj Array of pdf-data
1198
+	 */
1199
+	private function _getPageResources ($obj) { // $obj = /Page
1200
+		$obj = $this->getObjectVal($obj);
1201
+
1202
+		// If the current object has a resources
1203
+		// dictionary associated with it, we use
1204
+		// it. Otherwise, we move back to its
1205
+		// parent object.
1206
+		if (isset ($obj[1][1]['/Resources'])) {
1207
+			$res = $obj[1][1]['/Resources'];
1208
+			if ($res[0] == PDF_TYPE_OBJECT)
1209
+				return $res[1];
1210
+			return $res;
1211
+		} else {
1212
+			if (!isset ($obj[1][1]['/Parent'])) {
1213
+				return false;
1214
+			} else {
1215
+				$res = $this->_getPageResources($obj[1][1]['/Parent']);
1216
+				if ($res[0] == PDF_TYPE_OBJECT)
1217
+					return $res[1];
1218
+				return $res;
1219
+			}
1220
+		}
1221
+	}
1222
+
1223
+	/**
1224
+	 * Get annotations from current page
1225
+	 *
1226
+	 * @return array
1227
+	 */
1228
+	public function getPageAnnotations() {
1229
+		return $this->_getPageAnnotations($this->pages[$this->pageno]);
1230
+	}
1231
+
1232
+	/**
1233
+	 * Get annotations from /Page
1234
+	 *
1235
+	 * @param array $obj Array of pdf-data
1236
+	 */
1237
+	private function _getPageAnnotations ($obj) { // $obj = /Page
1238
+		$obj = $this->getObjectVal($obj);
1239
+
1240
+		// If the current object has an annotations
1241
+		// dictionary associated with it, we use
1242
+		// it. Otherwise, we move back to its
1243
+		// parent object.
1244
+		if (isset ($obj[1][1]['/Annots'])) {
1245
+			$annots = $obj[1][1]['/Annots'];
1246
+		} else {
1247
+			if (!isset ($obj[1][1]['/Parent'])) {
1248
+				return false;
1249
+			} else {
1250
+				$annots = $this->_getPageAnnotations($obj[1][1]['/Parent']);
1251
+			}
1252
+		}
1253
+
1254
+		if ($annots[0] == PDF_TYPE_OBJREF)
1255
+			return $this->getObjectVal($annots);
1256
+		return $annots;
1257
+	}
1258
+
1259
+
1260
+	/**
1261
+	 * Get content of current page
1262
+	 *
1263
+	 * If more /Contents is an array, the streams are concated
1264
+	 *
1265
+	 * @return string
1266
+	 */
1267
+	public function getContent() {
1268
+		$buffer = '';
1269
+
1270
+		if (isset($this->pages[$this->pageno][1][1]['/Contents'])) {
1271
+			$contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']);
1272
+			foreach($contents AS $tmp_content) {
1273
+				$buffer .= $this->_rebuildContentStream($tmp_content) . ' ';
1274
+			}
1275
+		}
1276
+
1277
+		return $buffer;
1278
+	}
1279
+
1280
+
1281
+	/**
1282
+	 * Resolve all content-objects
1283
+	 *
1284
+	 * @param array $content_ref
1285
+	 * @return array
1286
+	 */
1287
+	private function _getPageContent($content_ref) {
1288
+		$contents = array();
1289
+
1290
+		if ($content_ref[0] == PDF_TYPE_OBJREF) {
1291
+			$content = $this->getObjectVal($content_ref);
1292
+			if ($content[1][0] == PDF_TYPE_ARRAY) {
1293
+				$contents = $this->_getPageContent($content[1]);
1294
+			} else {
1295
+				$contents[] = $content;
1296
+			}
1297
+		} elseif ($content_ref[0] == PDF_TYPE_ARRAY) {
1298
+			foreach ($content_ref[1] AS $tmp_content_ref) {
1299
+				$contents = array_merge($contents,$this->_getPageContent($tmp_content_ref));
1300
+			}
1301
+		}
1302
+
1303
+		return $contents;
1304
+	}
1305
+
1306
+
1307
+	/**
1308
+	 * Rebuild content-streams
1309
+	 *
1310
+	 * @param array $obj
1311
+	 * @return string
1312
+	 */
1313
+	private function _rebuildContentStream($obj) {
1314
+		$filters = array();
1315
+
1316
+		if (isset($obj[1][1]['/Filter'])) {
1317
+			$_filter = $obj[1][1]['/Filter'];
1318
+
1319
+			if ($_filter[0] == PDF_TYPE_OBJREF) {
1320
+				$tmpFilter = $this->getObjectVal($_filter);
1321
+				$_filter = $tmpFilter[1];
1322
+			}
1323
+
1324
+			if ($_filter[0] == PDF_TYPE_TOKEN) {
1325
+				$filters[] = $_filter;
1326
+			} elseif ($_filter[0] == PDF_TYPE_ARRAY) {
1327
+				$filters = $_filter[1];
1328
+			}
1329
+		}
1330
+
1331
+		$stream = $obj[2][1];
1332
+
1333
+		foreach ($filters AS $_filter) {
1334
+			$stream = $this->FilterDecoders->decodeFilter($_filter[1], $stream);
1335
+		}
1336
+
1337
+		return $stream;
1338
+	}
1339
+
1340
+
1341
+	/**
1342
+	 * Get a Box from a page
1343
+	 * Arrayformat is same as used by fpdf_tpl
1344
+	 *
1345
+	 * @param array $page a /Page
1346
+	 * @param string $box_index Type of Box @see $availableBoxes
1347
+	 * @param float Scale factor from user space units to points
1348
+	 * @return array
1349
+	 */
1350
+	public function getPageBox($page, $box_index, $k) {
1351
+		$page = $this->getObjectVal($page);
1352
+		$box = null;
1353
+		if (isset($page[1][1][$box_index]))
1354
+			$box =& $page[1][1][$box_index];
1355
+
1356
+		if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) {
1357
+			$tmp_box = $this->getObjectVal($box);
1358
+			$box = $tmp_box[1];
1359
+		}
1360
+
1361
+		if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) {
1362
+			$b =& $box[1];
1363
+			return array('x' => $b[0][1] / $k,
1364
+						 'y' => $b[1][1] / $k,
1365
+						 'w' => abs($b[0][1] - $b[2][1]) / $k,
1366
+						 'h' => abs($b[1][1] - $b[3][1]) / $k,
1367
+						 'llx' => min($b[0][1], $b[2][1]) / $k,
1368
+						 'lly' => min($b[1][1], $b[3][1]) / $k,
1369
+						 'urx' => max($b[0][1], $b[2][1]) / $k,
1370
+						 'ury' => max($b[1][1], $b[3][1]) / $k,
1371
+						 );
1372
+		} elseif (!isset ($page[1][1]['/Parent'])) {
1373
+			return false;
1374
+		} else {
1375
+			return $this->getPageBox($this->getObjectVal($page[1][1]['/Parent']), $box_index, $k);
1376
+		}
1377
+	}
1378
+
1379
+	/**
1380
+	 * Get all page boxes by page no
1381
+	 *
1382
+	 * @param int The page number
1383
+	 * @param float Scale factor from user space units to points
1384
+	 * @return array
1385
+	 */
1386
+	public function getPageBoxes($pageno, $k) {
1387
+		return $this->_getPageBoxes($this->pages[$pageno - 1], $k);
1388
+	}
1389
+
1390
+	/**
1391
+	 * Get all boxes from /Page
1392
+	 *
1393
+	 * @param array a /Page
1394
+	 * @return array
1395
+	 */
1396
+	private function _getPageBoxes($page, $k) {
1397
+		$boxes = array();
1398
+
1399
+		foreach($this->availableBoxes AS $box) {
1400
+			if ($_box = $this->getPageBox($page, $box, $k)) {
1401
+				$boxes[$box] = $_box;
1402
+			}
1403
+		}
1404
+
1405
+		return $boxes;
1406
+	}
1407
+
1408
+	/**
1409
+	 * Get the page rotation by pageno
1410
+	 *
1411
+	 * @param integer $pageno
1412
+	 * @return array
1413
+	 */
1414
+	public function getPageRotation($pageno) {
1415
+		return $this->_getPageRotation($this->pages[$pageno - 1]);
1416
+	}
1417
+
1418
+	private function _getPageRotation($obj) { // $obj = /Page
1419
+		$obj = $this->getObjectVal($obj);
1420
+		if (isset ($obj[1][1]['/Rotate'])) {
1421
+			$res = $this->getObjectVal($obj[1][1]['/Rotate']);
1422
+			if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
1423
+				return $res[1];
1424
+			return $res;
1425
+		} else {
1426
+			if (!isset ($obj[1][1]['/Parent'])) {
1427
+				return false;
1428
+			} else {
1429
+				$res = $this->_getPageRotation($obj[1][1]['/Parent']);
1430
+				if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
1431
+					return $res[1];
1432
+				return $res;
1433
+			}
1434
+		}
1435
+	}
1436
+
1437
+	/**
1438
+	 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution.
1439
+	 * @param $msg (string) The error message
1440
+	 * @public
1441
+	 * @since 1.0.000 (2011-05-23)
1442
+	 */
1443
+	public function Error($msg) {
1444
+		// exit program and print error
1445
+		die("<strong>TCPDI_PARSER ERROR [{$this->uniqueid}]: </strong>".$msg);
1446
+	}
1447 1447
 
1448 1448
 } // END OF TCPDF_PARSER CLASS
1449 1449
 
Please login to merge, or discard this patch.
htdocs/includes/tcpdi/tcpdi.php 1 patch
Indentation   +797 added lines, -797 removed lines patch added patch discarded remove patch
@@ -27,681 +27,681 @@  discard block
 block discarded – undo
27 27
 
28 28
 
29 29
 class TCPDI extends FPDF_TPL {
30
-    /**
31
-     * Actual filename
32
-     * @var string
33
-     */
34
-    var $current_filename;
35
-
36
-    /**
37
-     * Parser-Objects
38
-     * @var array
39
-     */
40
-    var $parsers;
41
-
42
-    /**
43
-     * Current parser
44
-     * @var object
45
-     */
46
-    var $current_parser;
47
-
48
-    /**
49
-     * object stack
50
-     * @var array
51
-     */
52
-    var $_obj_stack;
53
-
54
-    /**
55
-     * done object stack
56
-     * @var array
57
-     */
58
-    var $_don_obj_stack;
59
-
60
-    /**
61
-     * Current Object Id.
62
-     * @var integer
63
-     */
64
-    var $_current_obj_id;
65
-
66
-    /**
67
-     * The name of the last imported page box
68
-     * @var string
69
-     */
70
-    var $lastUsedPageBox;
71
-
72
-    /**
73
-     * Cache for imported pages/template ids
74
-     * @var array
75
-     */
76
-    var $_importedPages = array();
77
-
78
-    /**
79
-     * Cache for imported page annotations
80
-     * @var array
81
-     */
82
-    var $_importedAnnots = array();
83
-
84
-    /**
85
-     * Number of TOC pages, used for annotation offset
86
-     * @var integer
87
-     */
88
-    var $_numTOCpages = 0;
89
-
90
-    /**
91
-     * First TOC page, used for annotation offset
92
-     * @var integer
93
-     */
94
-    var $_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
-    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
-    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
-    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
-    function getPDFVersion() {
30
+	/**
31
+	 * Actual filename
32
+	 * @var string
33
+	 */
34
+	var $current_filename;
35
+
36
+	/**
37
+	 * Parser-Objects
38
+	 * @var array
39
+	 */
40
+	var $parsers;
41
+
42
+	/**
43
+	 * Current parser
44
+	 * @var object
45
+	 */
46
+	var $current_parser;
47
+
48
+	/**
49
+	 * object stack
50
+	 * @var array
51
+	 */
52
+	var $_obj_stack;
53
+
54
+	/**
55
+	 * done object stack
56
+	 * @var array
57
+	 */
58
+	var $_don_obj_stack;
59
+
60
+	/**
61
+	 * Current Object Id.
62
+	 * @var integer
63
+	 */
64
+	var $_current_obj_id;
65
+
66
+	/**
67
+	 * The name of the last imported page box
68
+	 * @var string
69
+	 */
70
+	var $lastUsedPageBox;
71
+
72
+	/**
73
+	 * Cache for imported pages/template ids
74
+	 * @var array
75
+	 */
76
+	var $_importedPages = array();
77
+
78
+	/**
79
+	 * Cache for imported page annotations
80
+	 * @var array
81
+	 */
82
+	var $_importedAnnots = array();
83
+
84
+	/**
85
+	 * Number of TOC pages, used for annotation offset
86
+	 * @var integer
87
+	 */
88
+	var $_numTOCpages = 0;
89
+
90
+	/**
91
+	 * First TOC page, used for annotation offset
92
+	 * @var integer
93
+	 */
94
+	var $_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
+	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
+	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
+	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
+	function getPDFVersion() {
148 148
 		return $this->PDFVersion;
149 149
 	}
150 150
 
151 151
 	/**
152
-     * Set the PDF version
153
-     *
154
-     * @return string
155
-     */
152
+	 * Set the PDF version
153
+	 *
154
+	 * @return string
155
+	 */
156 156
 	function setPDFVersion($version = '1.7') {
157 157
 		$this->PDFVersion = $version;
158 158
 	}
159 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
-    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
-        return $this->tpl;
239
-    }
240
-
241
-    function setPageFormatFromTemplatePage($pageno, $orientation) {
242
-        $fn = $this->current_filename;
243
-        $parser =& $this->parsers[$fn];
244
-        $parser->setPageno($pageno);
245
-        $boxes = $parser->getPageBoxes($pageno, $this->k);
246
-        foreach ($boxes as $name => $box) {
247
-            if ($name[0] == '/') {
248
-                $boxes[substr($name, 1)] = $box;
249
-                unset($boxes[$name]);
250
-            }
251
-        }
252
-        $this->setPageFormat($boxes, $orientation);
253
-    }
254
-
255
-    /* Wrapper for AddPage() which tracks TOC pages to offset annotations later */
256
-    function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
257
-        if ($this->inxobj) {
258
-            // we are inside an XObject template
259
-            return;
260
-        }
261
-        parent::AddPage($orientation, $format, $keepmargins, $tocpage);
262
-        if ($this->tocpage) {
263
-            $this->_numTOCpages++;
264
-        }
265
-    }
266
-
267
-    /* Wrapper for AddTOC() which tracks TOC position to offset annotations later */
268
-    function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
269
-        if (!TCPDF_STATIC::empty_string($page)) {
270
-            $this->_TOCpagenum = $page;
271
-        } else {
272
-            $this->_TOCpagenum = $this->page;
273
-        }
274
-
275
-        parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color);
276
-    }
277
-
278
-    function importAnnotations($pageno) {
279
-        $fn = $this->current_filename;
280
-        $parser =& $this->parsers[$fn];
281
-        $parser->setPageno($pageno);
282
-        $annots = $parser->getPageAnnotations();
283
-
284
-        if (is_array($annots) && $annots[0] == PDF_TYPE_ARRAY // It's an array
285
-                && is_array($annots[1]) && count($annots[1]) > 1) // It's not empty - there are annotations for this page
286
-        {
287
-        	if (!isset($this->_obj_stack[$fn])) {
288
-                $this->_obj_stack[$fn] = array();
289
-            }
290
-
291
-            $this->_importedAnnots[$this->page] = array();
292
-            foreach ($annots[1] as $annot) {
293
-                $this->importAnnotation($annot);
294
-            }
295
-        }
296
-
297
-        if (is_array($annots) && $annots[0] == PDF_TYPE_OBJECT // We got an object
298
-                && is_array($annots[1]) && $annots[1][0] == PDF_TYPE_ARRAY // It's an array
299
-                && is_array($annots[1][1]) && count($annots[1][1]) > 1) // It's not empty - there are annotations for this page
300
-        {
301
-        	if (!isset($this->_obj_stack[$fn])) {
302
-                $this->_obj_stack[$fn] = array();
303
-            }
304
-
305
-            $this->_importedAnnots[$this->page] = array();
306
-            foreach ($annots[1][1] as $annot) {
307
-                $this->importAnnotation($annot);
308
-            }
309
-        }
310
-    }
311
-
312
-    function importAnnotation($annotation) {
313
-        $fn = $this->current_filename;
314
-        $old_id = $annotation[1];
315
-        $value = array(PDF_TYPE_OBJREF, $old_id, 0);
316
-        if (!isset($this->_don_obj_stack[$fn][$old_id])) {
317
-            $this->_newobj(false, true);
318
-            $this->_obj_stack[$fn][$old_id] = array($this->n, $value);
319
-            $this->_don_obj_stack[$fn][$old_id] = array($this->n, $value);
320
-        }
321
-        $objid = $this->_don_obj_stack[$fn][$old_id][0];
322
-        $this->_importedAnnots[$this->page][] = $objid;
323
-    }
324
-
325
-
326
-    /**
327
-     * Get references to page annotations.
328
-     * @param $n (int) page number
329
-     * @return string
330
-     * @protected
331
-     * @author Nicola Asuni
332
-     * @since 5.0.010 (2010-05-17)
333
-     */
334
-    protected function _getannotsrefs($n) {
335
-        if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) {
336
-            // Offset page number to account for TOC being inserted before page containing annotations.
337
-            $n -= $this->_numTOCpages;
338
-        }
339
-        if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
340
-            return '';
341
-        }
342
-        $out = ' /Annots [';
343
-        if (isset($this->_importedAnnots[$n])) {
344
-            foreach ($this->_importedAnnots[$n] as $key => $val) {
345
-                $out .= ' '.$val.' 0 R';
346
-            }
347
-        }
348
-        if (isset($this->PageAnnots[$n])) {
349
-            foreach ($this->PageAnnots[$n] as $key => $val) {
350
-                if (!in_array($val['n'], $this->radio_groups)) {
351
-                    $out .= ' '.$val['n'].' 0 R';
352
-                }
353
-            }
354
-            // add radiobutton groups
355
-            if (isset($this->radiobutton_groups[$n])) {
356
-                foreach ($this->radiobutton_groups[$n] as $key => $data) {
357
-                    if (isset($data['n'])) {
358
-                        $out .= ' '.$data['n'].' 0 R';
359
-                    }
360
-                }
361
-            }
362
-        }
363
-        if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
364
-            // set reference for signature object
365
-            $out .= ' '.$this->sig_obj_id.' 0 R';
366
-        }
367
-        if (!empty($this->empty_signature_appearance)) {
368
-            foreach ($this->empty_signature_appearance as $esa) {
369
-                if ($esa['page'] == $n) {
370
-                    // set reference for empty signature objects
371
-                    $out .= ' '.$esa['objid'].' 0 R';
372
-                }
373
-            }
374
-        }
375
-        $out .= ' ]';
376
-        return $out;
377
-    }
378
-
379
-
380
-    /**
381
-     * Returns the last used page box
382
-     *
383
-     * @return string
384
-     */
385
-    function getLastUsedPageBox() {
386
-        return $this->lastUsedPageBox;
387
-    }
388
-
389
-
390
-    function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
391
-        if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
392
-            $size = $this->getTemplateSize($tplidx, $_w, $_h);
393
-            $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
394
-            $size = array($size['w'], $size['h']);
395
-
396
-            $this->setPageFormat($size, $orientation);
397
-        }
398
-
399
-        $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
400
-        $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
401
-        $this->_out('Q');
402
-
403
-        return $s;
404
-    }
405
-
406
-    /**
407
-     * Private method, that rebuilds all needed objects of source files
408
-     */
409
-    function _putimportedobjects() {
410
-        if (is_array($this->parsers) && count($this->parsers) > 0) {
411
-            foreach($this->parsers AS $filename => $p) {
412
-                $this->current_parser =& $this->parsers[$filename];
413
-                if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
414
-                    while(($n = key($this->_obj_stack[$filename])) !== null) {
415
-                        $nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]);
416
-
417
-                        $this->_newobj($this->_obj_stack[$filename][$n][0]);
418
-
419
-                        if ($nObj[0] == PDF_TYPE_STREAM) {
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
+	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
+		return $this->tpl;
239
+	}
240
+
241
+	function setPageFormatFromTemplatePage($pageno, $orientation) {
242
+		$fn = $this->current_filename;
243
+		$parser =& $this->parsers[$fn];
244
+		$parser->setPageno($pageno);
245
+		$boxes = $parser->getPageBoxes($pageno, $this->k);
246
+		foreach ($boxes as $name => $box) {
247
+			if ($name[0] == '/') {
248
+				$boxes[substr($name, 1)] = $box;
249
+				unset($boxes[$name]);
250
+			}
251
+		}
252
+		$this->setPageFormat($boxes, $orientation);
253
+	}
254
+
255
+	/* Wrapper for AddPage() which tracks TOC pages to offset annotations later */
256
+	function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
257
+		if ($this->inxobj) {
258
+			// we are inside an XObject template
259
+			return;
260
+		}
261
+		parent::AddPage($orientation, $format, $keepmargins, $tocpage);
262
+		if ($this->tocpage) {
263
+			$this->_numTOCpages++;
264
+		}
265
+	}
266
+
267
+	/* Wrapper for AddTOC() which tracks TOC position to offset annotations later */
268
+	function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
269
+		if (!TCPDF_STATIC::empty_string($page)) {
270
+			$this->_TOCpagenum = $page;
271
+		} else {
272
+			$this->_TOCpagenum = $this->page;
273
+		}
274
+
275
+		parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color);
276
+	}
277
+
278
+	function importAnnotations($pageno) {
279
+		$fn = $this->current_filename;
280
+		$parser =& $this->parsers[$fn];
281
+		$parser->setPageno($pageno);
282
+		$annots = $parser->getPageAnnotations();
283
+
284
+		if (is_array($annots) && $annots[0] == PDF_TYPE_ARRAY // It's an array
285
+				&& is_array($annots[1]) && count($annots[1]) > 1) // It's not empty - there are annotations for this page
286
+		{
287
+			if (!isset($this->_obj_stack[$fn])) {
288
+				$this->_obj_stack[$fn] = array();
289
+			}
290
+
291
+			$this->_importedAnnots[$this->page] = array();
292
+			foreach ($annots[1] as $annot) {
293
+				$this->importAnnotation($annot);
294
+			}
295
+		}
296
+
297
+		if (is_array($annots) && $annots[0] == PDF_TYPE_OBJECT // We got an object
298
+				&& is_array($annots[1]) && $annots[1][0] == PDF_TYPE_ARRAY // It's an array
299
+				&& is_array($annots[1][1]) && count($annots[1][1]) > 1) // It's not empty - there are annotations for this page
300
+		{
301
+			if (!isset($this->_obj_stack[$fn])) {
302
+				$this->_obj_stack[$fn] = array();
303
+			}
304
+
305
+			$this->_importedAnnots[$this->page] = array();
306
+			foreach ($annots[1][1] as $annot) {
307
+				$this->importAnnotation($annot);
308
+			}
309
+		}
310
+	}
311
+
312
+	function importAnnotation($annotation) {
313
+		$fn = $this->current_filename;
314
+		$old_id = $annotation[1];
315
+		$value = array(PDF_TYPE_OBJREF, $old_id, 0);
316
+		if (!isset($this->_don_obj_stack[$fn][$old_id])) {
317
+			$this->_newobj(false, true);
318
+			$this->_obj_stack[$fn][$old_id] = array($this->n, $value);
319
+			$this->_don_obj_stack[$fn][$old_id] = array($this->n, $value);
320
+		}
321
+		$objid = $this->_don_obj_stack[$fn][$old_id][0];
322
+		$this->_importedAnnots[$this->page][] = $objid;
323
+	}
324
+
325
+
326
+	/**
327
+	 * Get references to page annotations.
328
+	 * @param $n (int) page number
329
+	 * @return string
330
+	 * @protected
331
+	 * @author Nicola Asuni
332
+	 * @since 5.0.010 (2010-05-17)
333
+	 */
334
+	protected function _getannotsrefs($n) {
335
+		if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) {
336
+			// Offset page number to account for TOC being inserted before page containing annotations.
337
+			$n -= $this->_numTOCpages;
338
+		}
339
+		if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
340
+			return '';
341
+		}
342
+		$out = ' /Annots [';
343
+		if (isset($this->_importedAnnots[$n])) {
344
+			foreach ($this->_importedAnnots[$n] as $key => $val) {
345
+				$out .= ' '.$val.' 0 R';
346
+			}
347
+		}
348
+		if (isset($this->PageAnnots[$n])) {
349
+			foreach ($this->PageAnnots[$n] as $key => $val) {
350
+				if (!in_array($val['n'], $this->radio_groups)) {
351
+					$out .= ' '.$val['n'].' 0 R';
352
+				}
353
+			}
354
+			// add radiobutton groups
355
+			if (isset($this->radiobutton_groups[$n])) {
356
+				foreach ($this->radiobutton_groups[$n] as $key => $data) {
357
+					if (isset($data['n'])) {
358
+						$out .= ' '.$data['n'].' 0 R';
359
+					}
360
+				}
361
+			}
362
+		}
363
+		if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
364
+			// set reference for signature object
365
+			$out .= ' '.$this->sig_obj_id.' 0 R';
366
+		}
367
+		if (!empty($this->empty_signature_appearance)) {
368
+			foreach ($this->empty_signature_appearance as $esa) {
369
+				if ($esa['page'] == $n) {
370
+					// set reference for empty signature objects
371
+					$out .= ' '.$esa['objid'].' 0 R';
372
+				}
373
+			}
374
+		}
375
+		$out .= ' ]';
376
+		return $out;
377
+	}
378
+
379
+
380
+	/**
381
+	 * Returns the last used page box
382
+	 *
383
+	 * @return string
384
+	 */
385
+	function getLastUsedPageBox() {
386
+		return $this->lastUsedPageBox;
387
+	}
388
+
389
+
390
+	function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
391
+		if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
392
+			$size = $this->getTemplateSize($tplidx, $_w, $_h);
393
+			$orientation = $size['w'] > $size['h'] ? 'L' : 'P';
394
+			$size = array($size['w'], $size['h']);
395
+
396
+			$this->setPageFormat($size, $orientation);
397
+		}
398
+
399
+		$this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
400
+		$s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
401
+		$this->_out('Q');
402
+
403
+		return $s;
404
+	}
405
+
406
+	/**
407
+	 * Private method, that rebuilds all needed objects of source files
408
+	 */
409
+	function _putimportedobjects() {
410
+		if (is_array($this->parsers) && count($this->parsers) > 0) {
411
+			foreach($this->parsers AS $filename => $p) {
412
+				$this->current_parser =& $this->parsers[$filename];
413
+				if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
414
+					while(($n = key($this->_obj_stack[$filename])) !== null) {
415
+						$nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]);
416
+
417
+						$this->_newobj($this->_obj_stack[$filename][$n][0]);
418
+
419
+						if ($nObj[0] == PDF_TYPE_STREAM) {
420 420
 							$this->pdf_write_value($nObj);
421
-                        } else {
422
-                            $this->pdf_write_value($nObj[1]);
423
-                        }
424
-
425
-                        $this->_out('endobj');
426
-                        $this->_obj_stack[$filename][$n] = null; // free memory
427
-                        unset($this->_obj_stack[$filename][$n]);
428
-                        reset($this->_obj_stack[$filename]);
429
-                    }
430
-                }
431
-
432
-                // We're done with this parser.  Clean it up to free a bit of RAM.
433
-                $this->current_parser->cleanUp();
434
-                unset($this->parsers[$filename]);
435
-            }
436
-        }
437
-    }
438
-
439
-
440
-    /**
441
-     * Private Method that writes the form xobjects
442
-     */
443
-    function _putformxobjects() {
444
-        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
445
-	    reset($this->tpls);
446
-        foreach($this->tpls AS $tplidx => $tpl) {
447
-            $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
448
-    		$this->_newobj();
449
-    		$cN = $this->n; // TCPDF/Protection: rem current "n"
450
-
451
-    		$this->tpls[$tplidx]['n'] = $this->n;
452
-    		$this->_out('<<' . $filter . '/Type /XObject');
453
-            $this->_out('/Subtype /Form');
454
-            $this->_out('/FormType 1');
455
-
456
-            $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
457
-                (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
458
-                (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
459
-                (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
460
-                (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
461
-            ));
462
-
463
-            $c = 1;
464
-            $s = 0;
465
-            $tx = 0;
466
-            $ty = 0;
467
-
468
-            if (isset($tpl['box'])) {
469
-                $tx = -$tpl['box']['llx'];
470
-                $ty = -$tpl['box']['lly'];
471
-
472
-                if ($tpl['_rotationAngle'] <> 0) {
473
-                    $angle = $tpl['_rotationAngle'] * M_PI/180;
474
-                    $c=cos($angle);
475
-                    $s=sin($angle);
476
-
477
-                    switch($tpl['_rotationAngle']) {
478
-                        case -90:
479
-                           $tx = -$tpl['box']['lly'];
480
-                           $ty = $tpl['box']['urx'];
481
-                           break;
482
-                        case -180:
483
-                            $tx = $tpl['box']['urx'];
484
-                            $ty = $tpl['box']['ury'];
485
-                            break;
486
-                        case -270:
487
-                        	$tx = $tpl['box']['ury'];
488
-                            $ty = -$tpl['box']['llx'];
489
-                            break;
490
-                    }
491
-                }
492
-            } elseif (!empty($tpl['x']) || !empty($tpl['y'])) {
493
-                $tx = -$tpl['x'] * 2;
494
-                $ty = $tpl['y'] * 2;
495
-            }
496
-
497
-            $tx *= $this->k;
498
-            $ty *= $this->k;
499
-
500
-            if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
501
-                $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
502
-                    $c, $s, -$s, $c, $tx, $ty
503
-                ));
504
-            }
505
-
506
-            $this->_out('/Resources ');
507
-
508
-            if (isset($tpl['resources'])) {
509
-                $this->current_parser =& $tpl['parser'];
510
-                $this->pdf_write_value($tpl['resources']); // "n" will be changed
511
-            } else {
512
-                $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
513
-            	if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
514
-                	$this->_out('/Font <<');
515
-                    foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
516
-                		$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
517
-                	$this->_out('>>');
518
-                }
519
-            	if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
520
-            	   isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
521
-            	{
522
-                    $this->_out('/XObject <<');
523
-                    if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
524
-                        foreach($this->_res['tpl'][$tplidx]['images'] as $image)
525
-                  			$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
526
-                    }
527
-                    if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
528
-                        foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
529
-                            $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
530
-                    }
531
-                    $this->_out('>>');
532
-            	}
533
-            	$this->_out('>>');
534
-            }
535
-
536
-            $this->_out('/Group <</Type/Group/S/Transparency>>');
537
-
538
-            $nN = $this->n; // TCPDF: rem new "n"
539
-            $this->n = $cN; // TCPDF: reset to current "n"
540
-
541
-        	$p = $this->_getrawstream($p);
542
-        	$this->_out('/Length ' . strlen($p) . ' >>');
543
-        	$this->_out("stream\n" . $p . "\nendstream");
544
-
545
-    		$this->_out('endobj');
546
-    		$this->n = $nN; // TCPDF: reset to new "n"
547
-        }
548
-
549
-        $this->_putimportedobjects();
550
-    }
551
-
552
-    /**
553
-     * Rewritten to handle existing own defined objects
554
-     */
555
-    function _newobj($obj_id = false, $onlynewobj = false) {
556
-        if (!$obj_id) {
557
-            $obj_id = ++$this->n;
558
-        }
559
-
560
-        //Begin a new object
561
-        if (!$onlynewobj) {
562
-            $this->offsets[$obj_id] = $this->bufferlen;
563
-            $this->_out($obj_id . ' 0 obj');
564
-            $this->_current_obj_id = $obj_id; // for later use with encryption
565
-        }
566
-
567
-        return $obj_id;
568
-    }
569
-
570
-    /**
571
-     * Writes a value
572
-     * Needed to rebuild the source document
573
-     *
574
-     * @param mixed $value A PDF-Value. Structure of values see cases in this method
575
-     */
576
-    function pdf_write_value(&$value)
577
-    {
578
-        switch ($value[0]) {
579
-            case PDF_TYPE_STRING:
580
-                if ($this->encrypted) {
581
-                    $value[1] = $this->_unescape($value[1]);
582
-                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
583
-                    $value[1] = TCPDF_STATIC::_escape($value[1]);
584
-                }
585
-                break;
586
-
587
-            case PDF_TYPE_STREAM:
588
-                if ($this->encrypted) {
589
-                    $value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
590
-                    $value[1][1]['/Length'] = array(
591
-                        PDF_TYPE_NUMERIC,
592
-                        strlen($value[2][1])
593
-                    );
594
-                }
595
-                break;
596
-
597
-            case PDF_TYPE_HEX:
598
-                if ($this->encrypted) {
599
-                    $value[1] = $this->hex2str($value[1]);
600
-                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
601
-
602
-                    // remake hexstring of encrypted string
603
-                    $value[1] = $this->str2hex($value[1]);
604
-                }
605
-                break;
606
-        }
607
-
608
-        switch ($value[0]) {
609
-
610
-            case PDF_TYPE_TOKEN:
611
-                $this->_straightOut('/'.$value[1] . ' ');
612
-                break;
613
-            case PDF_TYPE_NUMERIC:
614
-            case PDF_TYPE_REAL:
615
-                if (is_float($value[1]) && $value[1] != 0) {
616
-                    $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
617
-                } else {
618
-                    $this->_straightOut($value[1] . ' ');
619
-                }
620
-                break;
621
-
622
-            case PDF_TYPE_ARRAY:
623
-
624
-                // An array. Output the proper
625
-                // structure and move on.
626
-
627
-                $this->_straightOut('[');
628
-                for ($i = 0; $i < count($value[1]); $i++) {
629
-                    $this->pdf_write_value($value[1][$i]);
630
-                }
631
-
632
-                $this->_out(']');
633
-                break;
634
-
635
-            case PDF_TYPE_DICTIONARY:
636
-
637
-                // A dictionary.
638
-                $this->_straightOut('<<');
639
-
640
-                reset ($value[1]);
641
-
642
-                foreach ($value[1] as $k => $v) {
643
-                    $this->_straightOut($k . ' ');
644
-                    $this->pdf_write_value($v);
645
-                }
646
-
647
-                $this->_straightOut('>>');
648
-                break;
649
-
650
-            case PDF_TYPE_OBJREF:
651
-
652
-                // An indirect object reference
653
-                // Fill the object stack if needed
654
-                $cpfn =& $this->current_parser->uniqueid;
655
-
656
-                if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
657
-                    $this->_newobj(false, true);
658
-                    $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
659
-                    $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
660
-                }
661
-                $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
662
-
663
-                $this->_out($objid . ' 0 R');
664
-                break;
665
-
666
-            case PDF_TYPE_STRING:
667
-
668
-                // A string.
669
-                $this->_straightOut('(' . $value[1] . ')');
670
-
671
-                break;
672
-
673
-            case PDF_TYPE_STREAM:
674
-
675
-                // A stream. First, output the
676
-                // stream dictionary, then the
677
-                // stream data itself.
678
-                $this->pdf_write_value($value[1]);
679
-                $this->_out('stream');
680
-                $this->_out($value[2][1]);
681
-                $this->_out('endstream');
682
-                break;
683
-
684
-            case PDF_TYPE_HEX:
685
-                $this->_straightOut('<' . $value[1] . '>');
686
-                break;
687
-
688
-            case PDF_TYPE_BOOLEAN:
689
-                $this->_straightOut($value[1] ? 'true ' : 'false ');
690
-                break;
691
-
692
-            case PDF_TYPE_NULL:
693
-                // The null object.
421
+						} else {
422
+							$this->pdf_write_value($nObj[1]);
423
+						}
424
+
425
+						$this->_out('endobj');
426
+						$this->_obj_stack[$filename][$n] = null; // free memory
427
+						unset($this->_obj_stack[$filename][$n]);
428
+						reset($this->_obj_stack[$filename]);
429
+					}
430
+				}
431
+
432
+				// We're done with this parser.  Clean it up to free a bit of RAM.
433
+				$this->current_parser->cleanUp();
434
+				unset($this->parsers[$filename]);
435
+			}
436
+		}
437
+	}
438
+
439
+
440
+	/**
441
+	 * Private Method that writes the form xobjects
442
+	 */
443
+	function _putformxobjects() {
444
+		$filter=($this->compress) ? '/Filter /FlateDecode ' : '';
445
+		reset($this->tpls);
446
+		foreach($this->tpls AS $tplidx => $tpl) {
447
+			$p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
448
+			$this->_newobj();
449
+			$cN = $this->n; // TCPDF/Protection: rem current "n"
450
+
451
+			$this->tpls[$tplidx]['n'] = $this->n;
452
+			$this->_out('<<' . $filter . '/Type /XObject');
453
+			$this->_out('/Subtype /Form');
454
+			$this->_out('/FormType 1');
455
+
456
+			$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
457
+				(isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
458
+				(isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
459
+				(isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
460
+				(isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
461
+			));
462
+
463
+			$c = 1;
464
+			$s = 0;
465
+			$tx = 0;
466
+			$ty = 0;
467
+
468
+			if (isset($tpl['box'])) {
469
+				$tx = -$tpl['box']['llx'];
470
+				$ty = -$tpl['box']['lly'];
471
+
472
+				if ($tpl['_rotationAngle'] <> 0) {
473
+					$angle = $tpl['_rotationAngle'] * M_PI/180;
474
+					$c=cos($angle);
475
+					$s=sin($angle);
476
+
477
+					switch($tpl['_rotationAngle']) {
478
+						case -90:
479
+						   $tx = -$tpl['box']['lly'];
480
+						   $ty = $tpl['box']['urx'];
481
+						   break;
482
+						case -180:
483
+							$tx = $tpl['box']['urx'];
484
+							$ty = $tpl['box']['ury'];
485
+							break;
486
+						case -270:
487
+							$tx = $tpl['box']['ury'];
488
+							$ty = -$tpl['box']['llx'];
489
+							break;
490
+					}
491
+				}
492
+			} elseif (!empty($tpl['x']) || !empty($tpl['y'])) {
493
+				$tx = -$tpl['x'] * 2;
494
+				$ty = $tpl['y'] * 2;
495
+			}
496
+
497
+			$tx *= $this->k;
498
+			$ty *= $this->k;
499
+
500
+			if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
501
+				$this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
502
+					$c, $s, -$s, $c, $tx, $ty
503
+				));
504
+			}
505
+
506
+			$this->_out('/Resources ');
507
+
508
+			if (isset($tpl['resources'])) {
509
+				$this->current_parser =& $tpl['parser'];
510
+				$this->pdf_write_value($tpl['resources']); // "n" will be changed
511
+			} else {
512
+				$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
513
+				if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
514
+					$this->_out('/Font <<');
515
+					foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
516
+						$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
517
+					$this->_out('>>');
518
+				}
519
+				if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
520
+				   isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
521
+				{
522
+					$this->_out('/XObject <<');
523
+					if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
524
+						foreach($this->_res['tpl'][$tplidx]['images'] as $image)
525
+				  			$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
526
+					}
527
+					if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
528
+						foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
529
+							$this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
530
+					}
531
+					$this->_out('>>');
532
+				}
533
+				$this->_out('>>');
534
+			}
535
+
536
+			$this->_out('/Group <</Type/Group/S/Transparency>>');
537
+
538
+			$nN = $this->n; // TCPDF: rem new "n"
539
+			$this->n = $cN; // TCPDF: reset to current "n"
540
+
541
+			$p = $this->_getrawstream($p);
542
+			$this->_out('/Length ' . strlen($p) . ' >>');
543
+			$this->_out("stream\n" . $p . "\nendstream");
544
+
545
+			$this->_out('endobj');
546
+			$this->n = $nN; // TCPDF: reset to new "n"
547
+		}
548
+
549
+		$this->_putimportedobjects();
550
+	}
551
+
552
+	/**
553
+	 * Rewritten to handle existing own defined objects
554
+	 */
555
+	function _newobj($obj_id = false, $onlynewobj = false) {
556
+		if (!$obj_id) {
557
+			$obj_id = ++$this->n;
558
+		}
559
+
560
+		//Begin a new object
561
+		if (!$onlynewobj) {
562
+			$this->offsets[$obj_id] = $this->bufferlen;
563
+			$this->_out($obj_id . ' 0 obj');
564
+			$this->_current_obj_id = $obj_id; // for later use with encryption
565
+		}
566
+
567
+		return $obj_id;
568
+	}
569
+
570
+	/**
571
+	 * Writes a value
572
+	 * Needed to rebuild the source document
573
+	 *
574
+	 * @param mixed $value A PDF-Value. Structure of values see cases in this method
575
+	 */
576
+	function pdf_write_value(&$value)
577
+	{
578
+		switch ($value[0]) {
579
+			case PDF_TYPE_STRING:
580
+				if ($this->encrypted) {
581
+					$value[1] = $this->_unescape($value[1]);
582
+					$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
583
+					$value[1] = TCPDF_STATIC::_escape($value[1]);
584
+				}
585
+				break;
586
+
587
+			case PDF_TYPE_STREAM:
588
+				if ($this->encrypted) {
589
+					$value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
590
+					$value[1][1]['/Length'] = array(
591
+						PDF_TYPE_NUMERIC,
592
+						strlen($value[2][1])
593
+					);
594
+				}
595
+				break;
596
+
597
+			case PDF_TYPE_HEX:
598
+				if ($this->encrypted) {
599
+					$value[1] = $this->hex2str($value[1]);
600
+					$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
601
+
602
+					// remake hexstring of encrypted string
603
+					$value[1] = $this->str2hex($value[1]);
604
+				}
605
+				break;
606
+		}
607
+
608
+		switch ($value[0]) {
609
+
610
+			case PDF_TYPE_TOKEN:
611
+				$this->_straightOut('/'.$value[1] . ' ');
612
+				break;
613
+			case PDF_TYPE_NUMERIC:
614
+			case PDF_TYPE_REAL:
615
+				if (is_float($value[1]) && $value[1] != 0) {
616
+					$this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
617
+				} else {
618
+					$this->_straightOut($value[1] . ' ');
619
+				}
620
+				break;
621
+
622
+			case PDF_TYPE_ARRAY:
623
+
624
+				// An array. Output the proper
625
+				// structure and move on.
626
+
627
+				$this->_straightOut('[');
628
+				for ($i = 0; $i < count($value[1]); $i++) {
629
+					$this->pdf_write_value($value[1][$i]);
630
+				}
631
+
632
+				$this->_out(']');
633
+				break;
634
+
635
+			case PDF_TYPE_DICTIONARY:
636
+
637
+				// A dictionary.
638
+				$this->_straightOut('<<');
639
+
640
+				reset ($value[1]);
694 641
 
695
-                $this->_straightOut('null ');
696
-                break;
697
-        }
698
-    }
642
+				foreach ($value[1] as $k => $v) {
643
+					$this->_straightOut($k . ' ');
644
+					$this->pdf_write_value($v);
645
+				}
699 646
 
700
-    /**
701
-     * Modified so not each call will add a newline to the output.
702
-     */
703
-    function _straightOut($s) {
704
-        if ($this->state == 2) {
647
+				$this->_straightOut('>>');
648
+				break;
649
+
650
+			case PDF_TYPE_OBJREF:
651
+
652
+				// An indirect object reference
653
+				// Fill the object stack if needed
654
+				$cpfn =& $this->current_parser->uniqueid;
655
+
656
+				if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
657
+					$this->_newobj(false, true);
658
+					$this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
659
+					$this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
660
+				}
661
+				$objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
662
+
663
+				$this->_out($objid . ' 0 R');
664
+				break;
665
+
666
+			case PDF_TYPE_STRING:
667
+
668
+				// A string.
669
+				$this->_straightOut('(' . $value[1] . ')');
670
+
671
+				break;
672
+
673
+			case PDF_TYPE_STREAM:
674
+
675
+				// A stream. First, output the
676
+				// stream dictionary, then the
677
+				// stream data itself.
678
+				$this->pdf_write_value($value[1]);
679
+				$this->_out('stream');
680
+				$this->_out($value[2][1]);
681
+				$this->_out('endstream');
682
+				break;
683
+
684
+			case PDF_TYPE_HEX:
685
+				$this->_straightOut('<' . $value[1] . '>');
686
+				break;
687
+
688
+			case PDF_TYPE_BOOLEAN:
689
+				$this->_straightOut($value[1] ? 'true ' : 'false ');
690
+				break;
691
+
692
+			case PDF_TYPE_NULL:
693
+				// The null object.
694
+
695
+				$this->_straightOut('null ');
696
+				break;
697
+		}
698
+	}
699
+
700
+	/**
701
+	 * Modified so not each call will add a newline to the output.
702
+	 */
703
+	function _straightOut($s) {
704
+		if ($this->state == 2) {
705 705
 			if ($this->inxobj) {
706 706
 				// we are inside an XObject template
707 707
 				$this->xobjects[$this->xobjid]['outdata'] .= $s;
@@ -721,137 +721,137 @@  discard block
 block discarded – undo
721 721
 			// set general data
722 722
 			$this->setBuffer($s);
723 723
 		}
724
-    }
725
-
726
-    /**
727
-     * rewritten to close opened parsers
728
-     *
729
-     */
730
-    function _enddoc() {
731
-        parent::_enddoc();
732
-        $this->_closeParsers();
733
-    }
734
-
735
-    /**
736
-     * close all files opened by parsers
737
-     */
738
-    function _closeParsers() {
739
-        if ($this->state > 2 && is_array($this->parsers) && count($this->parsers) > 0) {
740
-          	$this->cleanUp();
741
-            return true;
742
-        }
743
-        return false;
744
-    }
745
-
746
-    /**
747
-     * Removes cylced references and closes the file handles of the parser objects
748
-     */
749
-    function cleanUp() {
750
-    	foreach ($this->parsers as $k => $_){
751
-        	$this->parsers[$k]->cleanUp();
752
-        	$this->parsers[$k] = null;
753
-        	unset($this->parsers[$k]);
754
-        }
755
-    }
756
-
757
-    // Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it
758
-    function _putstream($s, $n=0) {
759
-        $this->_out($this->_getstream($s, $n));
760
-    }
761
-
762
-    function _getxobjectdict() {
763
-        $out = parent::_getxobjectdict();
764
-        if (count($this->tpls)) {
765
-            foreach($this->tpls as $tplidx => $tpl) {
766
-                $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
767
-            }
768
-        }
769
-
770
-        return $out;
771
-    }
772
-
773
-    /**
774
-     * Unescapes a PDF string
775
-     *
776
-     * @param string $s
777
-     * @return string
778
-     */
779
-    function _unescape($s) {
780
-        $out = '';
781
-        for ($count = 0, $n = strlen($s); $count < $n; $count++) {
782
-            if ($s[$count] != '\\' || $count == $n-1) {
783
-                $out .= $s[$count];
784
-            } else {
785
-                switch ($s[++$count]) {
786
-                    case ')':
787
-                    case '(':
788
-                    case '\\':
789
-                        $out .= $s[$count];
790
-                        break;
791
-                    case 'f':
792
-                        $out .= chr(0x0C);
793
-                        break;
794
-                    case 'b':
795
-                        $out .= chr(0x08);
796
-                        break;
797
-                    case 't':
798
-                        $out .= chr(0x09);
799
-                        break;
800
-                    case 'r':
801
-                        $out .= chr(0x0D);
802
-                        break;
803
-                    case 'n':
804
-                        $out .= chr(0x0A);
805
-                        break;
806
-                    case "\r":
807
-                        if ($count != $n-1 && $s[$count+1] == "\n")
808
-                            $count++;
809
-                        break;
810
-                    case "\n":
811
-                        break;
812
-                    default:
813
-                        // Octal-Values
814
-                        if (ord($s[$count]) >= ord('0') &&
815
-                            ord($s[$count]) <= ord('9')) {
816
-                            $oct = ''. $s[$count];
817
-
818
-                            if (ord($s[$count+1]) >= ord('0') &&
819
-                                ord($s[$count+1]) <= ord('9')) {
820
-                                $oct .= $s[++$count];
821
-
822
-                                if (ord($s[$count+1]) >= ord('0') &&
823
-                                    ord($s[$count+1]) <= ord('9')) {
824
-                                    $oct .= $s[++$count];
825
-                                }
826
-                            }
827
-
828
-                            $out .= chr(octdec($oct));
829
-                        } else {
830
-                            $out .= $s[$count];
831
-                        }
832
-                }
833
-            }
834
-        }
835
-        return $out;
836
-    }
837
-
838
-    /**
839
-     * Hexadecimal to string
840
-     *
841
-     * @param string $hex
842
-     * @return string
843
-     */
844
-    function hex2str($hex) {
845
-        return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
846
-    }
847
-
848
-    /**
849
-     * String to hexadecimal
850
-     *
851
-     * @param string $str
852
-     * @return string
853
-     */
854
-    function str2hex($str) {
855
-        return current(unpack('H*', $str));
856
-    }
724
+	}
725
+
726
+	/**
727
+	 * rewritten to close opened parsers
728
+	 *
729
+	 */
730
+	function _enddoc() {
731
+		parent::_enddoc();
732
+		$this->_closeParsers();
733
+	}
734
+
735
+	/**
736
+	 * close all files opened by parsers
737
+	 */
738
+	function _closeParsers() {
739
+		if ($this->state > 2 && is_array($this->parsers) && count($this->parsers) > 0) {
740
+		  	$this->cleanUp();
741
+			return true;
742
+		}
743
+		return false;
744
+	}
745
+
746
+	/**
747
+	 * Removes cylced references and closes the file handles of the parser objects
748
+	 */
749
+	function cleanUp() {
750
+		foreach ($this->parsers as $k => $_){
751
+			$this->parsers[$k]->cleanUp();
752
+			$this->parsers[$k] = null;
753
+			unset($this->parsers[$k]);
754
+		}
755
+	}
756
+
757
+	// Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it
758
+	function _putstream($s, $n=0) {
759
+		$this->_out($this->_getstream($s, $n));
760
+	}
761
+
762
+	function _getxobjectdict() {
763
+		$out = parent::_getxobjectdict();
764
+		if (count($this->tpls)) {
765
+			foreach($this->tpls as $tplidx => $tpl) {
766
+				$out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
767
+			}
768
+		}
769
+
770
+		return $out;
771
+	}
772
+
773
+	/**
774
+	 * Unescapes a PDF string
775
+	 *
776
+	 * @param string $s
777
+	 * @return string
778
+	 */
779
+	function _unescape($s) {
780
+		$out = '';
781
+		for ($count = 0, $n = strlen($s); $count < $n; $count++) {
782
+			if ($s[$count] != '\\' || $count == $n-1) {
783
+				$out .= $s[$count];
784
+			} else {
785
+				switch ($s[++$count]) {
786
+					case ')':
787
+					case '(':
788
+					case '\\':
789
+						$out .= $s[$count];
790
+						break;
791
+					case 'f':
792
+						$out .= chr(0x0C);
793
+						break;
794
+					case 'b':
795
+						$out .= chr(0x08);
796
+						break;
797
+					case 't':
798
+						$out .= chr(0x09);
799
+						break;
800
+					case 'r':
801
+						$out .= chr(0x0D);
802
+						break;
803
+					case 'n':
804
+						$out .= chr(0x0A);
805
+						break;
806
+					case "\r":
807
+						if ($count != $n-1 && $s[$count+1] == "\n")
808
+							$count++;
809
+						break;
810
+					case "\n":
811
+						break;
812
+					default:
813
+						// Octal-Values
814
+						if (ord($s[$count]) >= ord('0') &&
815
+							ord($s[$count]) <= ord('9')) {
816
+							$oct = ''. $s[$count];
817
+
818
+							if (ord($s[$count+1]) >= ord('0') &&
819
+								ord($s[$count+1]) <= ord('9')) {
820
+								$oct .= $s[++$count];
821
+
822
+								if (ord($s[$count+1]) >= ord('0') &&
823
+									ord($s[$count+1]) <= ord('9')) {
824
+									$oct .= $s[++$count];
825
+								}
826
+							}
827
+
828
+							$out .= chr(octdec($oct));
829
+						} else {
830
+							$out .= $s[$count];
831
+						}
832
+				}
833
+			}
834
+		}
835
+		return $out;
836
+	}
837
+
838
+	/**
839
+	 * Hexadecimal to string
840
+	 *
841
+	 * @param string $hex
842
+	 * @return string
843
+	 */
844
+	function hex2str($hex) {
845
+		return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
846
+	}
847
+
848
+	/**
849
+	 * String to hexadecimal
850
+	 *
851
+	 * @param string $str
852
+	 * @return string
853
+	 */
854
+	function str2hex($str) {
855
+		return current(unpack('H*', $str));
856
+	}
857 857
 }
Please login to merge, or discard this patch.