PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / tecnickcom / tcpdf / tcpdf_parser.php
matomo / app / vendor / tecnickcom / tcpdf Last commit date
config 2 years ago fonts 2 years ago include 1 year ago LICENSE.TXT 2 years ago README.md 2 years ago VERSION 1 year ago tcpdf.php 1 year ago tcpdf_autoconfig.php 2 years ago tcpdf_barcodes_1d.php 2 years ago tcpdf_barcodes_2d.php 2 years ago tcpdf_import.php 2 years ago tcpdf_parser.php 2 years ago
tcpdf_parser.php
812 lines
1 <?php
2
3 namespace {
4 //============================================================+
5 // File name : tcpdf_parser.php
6 // Version : 1.0.16
7 // Begin : 2011-05-23
8 // Last Update : 2015-04-28
9 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
10 // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3
11 // -------------------------------------------------------------------
12 // Copyright (C) 2011-2015 Nicola Asuni - Tecnick.com LTD
13 //
14 // This file is part of TCPDF software library.
15 //
16 // TCPDF is free software: you can redistribute it and/or modify it
17 // under the terms of the GNU Lesser General Public License as
18 // published by the Free Software Foundation, either version 3 of the
19 // License, or (at your option) any later version.
20 //
21 // TCPDF is distributed in the hope that it will be useful, but
22 // WITHOUT ANY WARRANTY; without even the implied warranty of
23 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 // See the GNU Lesser General Public License for more details.
25 //
26 // You should have received a copy of the License
27 // along with TCPDF. If not, see
28 // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
29 //
30 // See LICENSE.TXT file for more information.
31 // -------------------------------------------------------------------
32 //
33 // Description : This is a PHP class for parsing PDF documents.
34 //
35 //============================================================+
36 /**
37 * @file
38 * This is a PHP class for parsing PDF documents.<br>
39 * @package com.tecnick.tcpdf
40 * @author Nicola Asuni
41 * @version 1.0.15
42 */
43 // include class for decoding filters
44 require_once \dirname(__FILE__) . '/include/tcpdf_filters.php';
45 /**
46 * @class TCPDF_PARSER
47 * This is a PHP class for parsing PDF documents.<br>
48 * @package com.tecnick.tcpdf
49 * @brief This is a PHP class for parsing PDF documents..
50 * @version 1.0.15
51 * @author Nicola Asuni - info@tecnick.com
52 */
53 class TCPDF_PARSER
54 {
55 /**
56 * Raw content of the PDF document.
57 * @private
58 */
59 private $pdfdata = '';
60 /**
61 * XREF data.
62 * @protected
63 */
64 protected $xref = array();
65 /**
66 * Array of PDF objects.
67 * @protected
68 */
69 protected $objects = array();
70 /**
71 * Class object for decoding filters.
72 * @private
73 */
74 private $FilterDecoders;
75 /**
76 * Array of configuration parameters.
77 * @private
78 */
79 private $cfg = array('die_for_errors' => \false, 'ignore_filter_decoding_errors' => \true, 'ignore_missing_filter_decoders' => \true);
80 // -----------------------------------------------------------------------------
81 /**
82 * Parse a PDF document an return an array of objects.
83 * @param string $data PDF data to parse.
84 * @param array $cfg Array of configuration parameters:
85 * 'die_for_errors' : if true termitate the program execution in case of error, otherwise thows an exception;
86 * 'ignore_filter_decoding_errors' : if true ignore filter decoding errors;
87 * 'ignore_missing_filter_decoders' : if true ignore missing filter decoding errors.
88 * @public
89 * @since 1.0.000 (2011-05-24)
90 */
91 public function __construct($data, $cfg = array())
92 {
93 if (empty($data)) {
94 $this->Error('Empty PDF data.');
95 }
96 // find the pdf header starting position
97 if (($trimpos = \strpos($data, '%PDF-')) === \FALSE) {
98 $this->Error('Invalid PDF data: missing %PDF header.');
99 }
100 // get PDF content string
101 $this->pdfdata = \substr($data, $trimpos);
102 // get length
103 $pdflen = \strlen($this->pdfdata);
104 // set configuration parameters
105 $this->setConfig($cfg);
106 // get xref and trailer data
107 $this->xref = $this->getXrefData();
108 // parse all document objects
109 $this->objects = array();
110 foreach ($this->xref['xref'] as $obj => $offset) {
111 if (!isset($this->objects[$obj]) and $offset > 0) {
112 // decode objects with positive offset
113 $this->objects[$obj] = $this->getIndirectObject($obj, $offset, \true);
114 }
115 }
116 // release some memory
117 unset($this->pdfdata);
118 $this->pdfdata = '';
119 }
120 /**
121 * Set the configuration parameters.
122 * @param array $cfg Array of configuration parameters:
123 * 'die_for_errors' : if true termitate the program execution in case of error, otherwise thows an exception;
124 * 'ignore_filter_decoding_errors' : if true ignore filter decoding errors;
125 * 'ignore_missing_filter_decoders' : if true ignore missing filter decoding errors.
126 * @public
127 */
128 protected function setConfig($cfg)
129 {
130 if (isset($cfg['die_for_errors'])) {
131 $this->cfg['die_for_errors'] = !!$cfg['die_for_errors'];
132 }
133 if (isset($cfg['ignore_filter_decoding_errors'])) {
134 $this->cfg['ignore_filter_decoding_errors'] = !!$cfg['ignore_filter_decoding_errors'];
135 }
136 if (isset($cfg['ignore_missing_filter_decoders'])) {
137 $this->cfg['ignore_missing_filter_decoders'] = !!$cfg['ignore_missing_filter_decoders'];
138 }
139 }
140 /**
141 * Return an array of parsed PDF document objects.
142 * @return array Array of parsed PDF document objects.
143 * @public
144 * @since 1.0.000 (2011-06-26)
145 */
146 public function getParsedData()
147 {
148 return array($this->xref, $this->objects);
149 }
150 /**
151 * Get Cross-Reference (xref) table and trailer data from PDF document data.
152 * @param int $offset xref offset (if know).
153 * @param array $xref previous xref array (if any).
154 * @return array containing xref and trailer data.
155 * @protected
156 * @since 1.0.000 (2011-05-24)
157 */
158 protected function getXrefData($offset = 0, $xref = array())
159 {
160 if ($offset == 0) {
161 // find last startxref
162 if (\preg_match_all('/[\\r\\n]startxref[\\s]*[\\r\\n]+([0-9]+)[\\s]*[\\r\\n]+%%EOF/i', $this->pdfdata, $matches, \PREG_SET_ORDER, $offset) == 0) {
163 $this->Error('Unable to find startxref');
164 }
165 $matches = \array_pop($matches);
166 $startxref = $matches[1];
167 } elseif (\strpos($this->pdfdata, 'xref', $offset) == $offset) {
168 // Already pointing at the xref table
169 $startxref = $offset;
170 } elseif (\preg_match('/([0-9]+[\\s][0-9]+[\\s]obj)/i', $this->pdfdata, $matches, \PREG_OFFSET_CAPTURE, $offset)) {
171 // Cross-Reference Stream object
172 $startxref = $offset;
173 } elseif (\preg_match('/[\\r\\n]startxref[\\s]*[\\r\\n]+([0-9]+)[\\s]*[\\r\\n]+%%EOF/i', $this->pdfdata, $matches, \PREG_OFFSET_CAPTURE, $offset)) {
174 // startxref found
175 $startxref = $matches[1][0];
176 } else {
177 $this->Error('Unable to find startxref');
178 }
179 // check xref position
180 if (\strpos($this->pdfdata, 'xref', $startxref) == $startxref) {
181 // Cross-Reference
182 $xref = $this->decodeXref($startxref, $xref);
183 } else {
184 // Cross-Reference Stream
185 $xref = $this->decodeXrefStream($startxref, $xref);
186 }
187 if (empty($xref)) {
188 $this->Error('Unable to find xref');
189 }
190 return $xref;
191 }
192 /**
193 * Decode the Cross-Reference section
194 * @param int $startxref Offset at which the xref section starts (position of the 'xref' keyword).
195 * @param array $xref Previous xref array (if any).
196 * @return array containing xref and trailer data.
197 * @protected
198 * @since 1.0.000 (2011-06-20)
199 */
200 protected function decodeXref($startxref, $xref = array())
201 {
202 $startxref += 4;
203 // 4 is the length of the word 'xref'
204 // 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)
205 $offset = $startxref + \strspn($this->pdfdata, "\x00\t\n\f\r ", $startxref);
206 // initialize object number
207 $obj_num = 0;
208 // search for cross-reference entries or subsection
209 while (\preg_match('/([0-9]+)[\\x20]([0-9]+)[\\x20]?([nf]?)(\\r\\n|[\\x20]?[\\r\\n])/', $this->pdfdata, $matches, \PREG_OFFSET_CAPTURE, $offset) > 0) {
210 if ($matches[0][1] != $offset) {
211 // we are on another section
212 break;
213 }
214 $offset += \strlen($matches[0][0]);
215 if ($matches[3][0] == 'n') {
216 // create unique object index: [object number]_[generation number]
217 $index = $obj_num . '_' . \intval($matches[2][0]);
218 // check if object already exist
219 if (!isset($xref['xref'][$index])) {
220 // store object offset position
221 $xref['xref'][$index] = \intval($matches[1][0]);
222 }
223 ++$obj_num;
224 } elseif ($matches[3][0] == 'f') {
225 ++$obj_num;
226 } else {
227 // object number (index)
228 $obj_num = \intval($matches[1][0]);
229 }
230 }
231 // get trailer data
232 if (\preg_match('/trailer[\\s]*<<(.*)>>/isU', $this->pdfdata, $matches, \PREG_OFFSET_CAPTURE, $offset) > 0) {
233 $trailer_data = $matches[1][0];
234 if (!isset($xref['trailer']) or empty($xref['trailer'])) {
235 // get only the last updated version
236 $xref['trailer'] = array();
237 // parse trailer_data
238 if (\preg_match('/Size[\\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
239 $xref['trailer']['size'] = \intval($matches[1]);
240 }
241 if (\preg_match('/Root[\\s]+([0-9]+)[\\s]+([0-9]+)[\\s]+R/i', $trailer_data, $matches) > 0) {
242 $xref['trailer']['root'] = \intval($matches[1]) . '_' . \intval($matches[2]);
243 }
244 if (\preg_match('/Encrypt[\\s]+([0-9]+)[\\s]+([0-9]+)[\\s]+R/i', $trailer_data, $matches) > 0) {
245 $xref['trailer']['encrypt'] = \intval($matches[1]) . '_' . \intval($matches[2]);
246 }
247 if (\preg_match('/Info[\\s]+([0-9]+)[\\s]+([0-9]+)[\\s]+R/i', $trailer_data, $matches) > 0) {
248 $xref['trailer']['info'] = \intval($matches[1]) . '_' . \intval($matches[2]);
249 }
250 if (\preg_match('/ID[\\s]*[\\[][\\s]*[<]([^>]*)[>][\\s]*[<]([^>]*)[>]/i', $trailer_data, $matches) > 0) {
251 $xref['trailer']['id'] = array();
252 $xref['trailer']['id'][0] = $matches[1];
253 $xref['trailer']['id'][1] = $matches[2];
254 }
255 }
256 if (\preg_match('/Prev[\\s]+([0-9]+)/i', $trailer_data, $matches) > 0) {
257 // get previous xref
258 $xref = $this->getXrefData(\intval($matches[1]), $xref);
259 }
260 } else {
261 $this->Error('Unable to find trailer');
262 }
263 return $xref;
264 }
265 /**
266 * Decode the Cross-Reference Stream section
267 * @param int $startxref Offset at which the xref section starts.
268 * @param array $xref Previous xref array (if any).
269 * @return array containing xref and trailer data.
270 * @protected
271 * @since 1.0.003 (2013-03-16)
272 */
273 protected function decodeXrefStream($startxref, $xref = array())
274 {
275 // try to read Cross-Reference Stream
276 $xrefobj = $this->getRawObject($startxref);
277 $xrefcrs = $this->getIndirectObject($xrefobj[1], $startxref, \true);
278 if (!isset($xref['trailer']) or empty($xref['trailer'])) {
279 // get only the last updated version
280 $xref['trailer'] = array();
281 $filltrailer = \true;
282 } else {
283 $filltrailer = \false;
284 }
285 if (!isset($xref['xref'])) {
286 $xref['xref'] = array();
287 }
288 $valid_crs = \false;
289 $columns = 0;
290 $sarr = $xrefcrs[0][1];
291 if (!\is_array($sarr)) {
292 $sarr = array();
293 }
294 foreach ($sarr as $k => $v) {
295 if ($v[0] == '/' and $v[1] == 'Type' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == '/' and $sarr[$k + 1][1] == 'XRef')) {
296 $valid_crs = \true;
297 } elseif ($v[0] == '/' and $v[1] == 'Index' and isset($sarr[$k + 1])) {
298 // first object number in the subsection
299 $index_first = \intval($sarr[$k + 1][1][0][1]);
300 // number of entries in the subsection
301 $index_entries = \intval($sarr[$k + 1][1][1][1]);
302 } elseif ($v[0] == '/' and $v[1] == 'Prev' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == 'numeric')) {
303 // get previous xref offset
304 $prevxref = \intval($sarr[$k + 1][1]);
305 } elseif ($v[0] == '/' and $v[1] == 'W' and isset($sarr[$k + 1])) {
306 // number of bytes (in the decoded stream) of the corresponding field
307 $wb = array();
308 $wb[0] = \intval($sarr[$k + 1][1][0][1]);
309 $wb[1] = \intval($sarr[$k + 1][1][1][1]);
310 $wb[2] = \intval($sarr[$k + 1][1][2][1]);
311 } elseif ($v[0] == '/' and $v[1] == 'DecodeParms' and isset($sarr[$k + 1][1])) {
312 $decpar = $sarr[$k + 1][1];
313 foreach ($decpar as $kdc => $vdc) {
314 if ($vdc[0] == '/' and $vdc[1] == 'Columns' and (isset($decpar[$kdc + 1]) and $decpar[$kdc + 1][0] == 'numeric')) {
315 $columns = \intval($decpar[$kdc + 1][1]);
316 } elseif ($vdc[0] == '/' and $vdc[1] == 'Predictor' and (isset($decpar[$kdc + 1]) and $decpar[$kdc + 1][0] == 'numeric')) {
317 $predictor = \intval($decpar[$kdc + 1][1]);
318 }
319 }
320 } elseif ($filltrailer) {
321 if ($v[0] == '/' and $v[1] == 'Size' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == 'numeric')) {
322 $xref['trailer']['size'] = $sarr[$k + 1][1];
323 } elseif ($v[0] == '/' and $v[1] == 'Root' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == 'objref')) {
324 $xref['trailer']['root'] = $sarr[$k + 1][1];
325 } elseif ($v[0] == '/' and $v[1] == 'Info' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == 'objref')) {
326 $xref['trailer']['info'] = $sarr[$k + 1][1];
327 } elseif ($v[0] == '/' and $v[1] == 'Encrypt' and (isset($sarr[$k + 1]) and $sarr[$k + 1][0] == 'objref')) {
328 $xref['trailer']['encrypt'] = $sarr[$k + 1][1];
329 } elseif ($v[0] == '/' and $v[1] == 'ID' and isset($sarr[$k + 1])) {
330 $xref['trailer']['id'] = array();
331 $xref['trailer']['id'][0] = $sarr[$k + 1][1][0][1];
332 $xref['trailer']['id'][1] = $sarr[$k + 1][1][1][1];
333 }
334 }
335 }
336 // decode data
337 if ($valid_crs and isset($xrefcrs[1][3][0])) {
338 // number of bytes in a row
339 $rowlen = $columns + 1;
340 // convert the stream into an array of integers
341 $sdata = \unpack('C*', $xrefcrs[1][3][0]);
342 // split the rows
343 $sdata = \array_chunk($sdata, $rowlen);
344 // initialize decoded array
345 $ddata = array();
346 // initialize first row with zeros
347 $prev_row = \array_fill(0, $rowlen, 0);
348 // for each row apply PNG unpredictor
349 foreach ($sdata as $k => $row) {
350 // initialize new row
351 $ddata[$k] = array();
352 // get PNG predictor value
353 $predictor = 10 + $row[0];
354 // for each byte on the row
355 for ($i = 1; $i <= $columns; ++$i) {
356 // new index
357 $j = $i - 1;
358 $row_up = $prev_row[$j];
359 if ($i == 1) {
360 $row_left = 0;
361 $row_upleft = 0;
362 } else {
363 $row_left = $row[$i - 1];
364 $row_upleft = $prev_row[$j - 1];
365 }
366 switch ($predictor) {
367 case 10:
368 // PNG prediction (on encoding, PNG None on all rows)
369 $ddata[$k][$j] = $row[$i];
370 break;
371 case 11:
372 // PNG prediction (on encoding, PNG Sub on all rows)
373 $ddata[$k][$j] = $row[$i] + $row_left & 0xff;
374 break;
375 case 12:
376 // PNG prediction (on encoding, PNG Up on all rows)
377 $ddata[$k][$j] = $row[$i] + $row_up & 0xff;
378 break;
379 case 13:
380 // PNG prediction (on encoding, PNG Average on all rows)
381 $ddata[$k][$j] = $row[$i] + ($row_left + $row_up) / 2 & 0xff;
382 break;
383 case 14:
384 // PNG prediction (on encoding, PNG Paeth on all rows)
385 // initial estimate
386 $p = $row_left + $row_up - $row_upleft;
387 // distances
388 $pa = \abs($p - $row_left);
389 $pb = \abs($p - $row_up);
390 $pc = \abs($p - $row_upleft);
391 $pmin = \min($pa, $pb, $pc);
392 // return minimum distance
393 switch ($pmin) {
394 case $pa:
395 $ddata[$k][$j] = $row[$i] + $row_left & 0xff;
396 break;
397 case $pb:
398 $ddata[$k][$j] = $row[$i] + $row_up & 0xff;
399 break;
400 case $pc:
401 $ddata[$k][$j] = $row[$i] + $row_upleft & 0xff;
402 break;
403 }
404 break;
405 default:
406 // PNG prediction (on encoding, PNG optimum)
407 $this->Error('Unknown PNG predictor');
408 break;
409 }
410 }
411 $prev_row = $ddata[$k];
412 }
413 // end for each row
414 // complete decoding
415 $sdata = array();
416 // for every row
417 foreach ($ddata as $k => $row) {
418 // initialize new row
419 $sdata[$k] = array(0, 0, 0);
420 if ($wb[0] == 0) {
421 // default type field
422 $sdata[$k][0] = 1;
423 }
424 $i = 0;
425 // count bytes in the row
426 // for every column
427 for ($c = 0; $c < 3; ++$c) {
428 // for every byte on the column
429 for ($b = 0; $b < $wb[$c]; ++$b) {
430 if (isset($row[$i])) {
431 $sdata[$k][$c] += $row[$i] << ($wb[$c] - 1 - $b) * 8;
432 }
433 ++$i;
434 }
435 }
436 }
437 $ddata = array();
438 // fill xref
439 if (isset($index_first)) {
440 $obj_num = $index_first;
441 } else {
442 $obj_num = 0;
443 }
444 foreach ($sdata as $k => $row) {
445 switch ($row[0]) {
446 case 0:
447 // (f) linked list of free objects
448 break;
449 case 1:
450 // (n) objects that are in use but are not compressed
451 // create unique object index: [object number]_[generation number]
452 $index = $obj_num . '_' . $row[2];
453 // check if object already exist
454 if (!isset($xref['xref'][$index])) {
455 // store object offset position
456 $xref['xref'][$index] = $row[1];
457 }
458 break;
459 case 2:
460 // compressed objects
461 // $row[1] = object number of the object stream in which this object is stored
462 // $row[2] = index of this object within the object stream
463 $index = $row[1] . '_0_' . $row[2];
464 $xref['xref'][$index] = -1;
465 break;
466 default:
467 // null objects
468 break;
469 }
470 ++$obj_num;
471 }
472 }
473 // end decoding data
474 if (isset($prevxref)) {
475 // get previous xref
476 $xref = $this->getXrefData($prevxref, $xref);
477 }
478 return $xref;
479 }
480 /**
481 * Get object type, raw value and offset to next object
482 * @param int $offset Object offset.
483 * @return array containing object type, raw value and offset to next object
484 * @protected
485 * @since 1.0.000 (2011-06-20)
486 */
487 protected function getRawObject($offset = 0)
488 {
489 $objtype = '';
490 // object type to be returned
491 $objval = '';
492 // object value to be returned
493 // 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)
494 $offset += \strspn($this->pdfdata, "\x00\t\n\f\r ", $offset);
495 // get first char
496 $char = $this->pdfdata[$offset];
497 // get object type
498 switch ($char) {
499 case '%':
500 // \x25 PERCENT SIGN
501 // skip comment and search for next token
502 $next = \strcspn($this->pdfdata, "\r\n", $offset);
503 if ($next > 0) {
504 $offset += $next;
505 return $this->getRawObject($offset);
506 }
507 break;
508 case '/':
509 // \x2F SOLIDUS
510 // name object
511 $objtype = $char;
512 ++$offset;
513 if (\preg_match('/^([^\\x00\\x09\\x0a\\x0c\\x0d\\x20\\s\\x28\\x29\\x3c\\x3e\\x5b\\x5d\\x7b\\x7d\\x2f\\x25]+)/', \substr($this->pdfdata, $offset, 256), $matches) == 1) {
514 $objval = $matches[1];
515 // unescaped value
516 $offset += \strlen($objval);
517 }
518 break;
519 case '(':
520 // \x28 LEFT PARENTHESIS
521 case ')':
522 // \x29 RIGHT PARENTHESIS
523 // literal string object
524 $objtype = $char;
525 ++$offset;
526 $strpos = $offset;
527 if ($char == '(') {
528 $open_bracket = 1;
529 while ($open_bracket > 0) {
530 if (!isset($this->pdfdata[$strpos])) {
531 break;
532 }
533 $ch = $this->pdfdata[$strpos];
534 switch ($ch) {
535 case '\\':
536 // REVERSE SOLIDUS (5Ch) (Backslash)
537 // skip next character
538 ++$strpos;
539 break;
540 case '(':
541 // LEFT PARENHESIS (28h)
542 ++$open_bracket;
543 break;
544 case ')':
545 // RIGHT PARENTHESIS (29h)
546 --$open_bracket;
547 break;
548 }
549 ++$strpos;
550 }
551 $objval = \substr($this->pdfdata, $offset, $strpos - $offset - 1);
552 $offset = $strpos;
553 }
554 break;
555 case '[':
556 // \x5B LEFT SQUARE BRACKET
557 case ']':
558 // \x5D RIGHT SQUARE BRACKET
559 // array object
560 $objtype = $char;
561 ++$offset;
562 if ($char == '[') {
563 // get array content
564 $objval = array();
565 do {
566 // get element
567 $element = $this->getRawObject($offset);
568 $offset = $element[2];
569 $objval[] = $element;
570 } while ($element[0] != ']');
571 // remove closing delimiter
572 \array_pop($objval);
573 }
574 break;
575 case '<':
576 // \x3C LESS-THAN SIGN
577 case '>':
578 // \x3E GREATER-THAN SIGN
579 if (isset($this->pdfdata[$offset + 1]) and $this->pdfdata[$offset + 1] == $char) {
580 // dictionary object
581 $objtype = $char . $char;
582 $offset += 2;
583 if ($char == '<') {
584 // get array content
585 $objval = array();
586 do {
587 // get element
588 $element = $this->getRawObject($offset);
589 $offset = $element[2];
590 $objval[] = $element;
591 } while ($element[0] != '>>');
592 // remove closing delimiter
593 \array_pop($objval);
594 }
595 } else {
596 // hexadecimal string object
597 $objtype = $char;
598 ++$offset;
599 if ($char == '<' and \preg_match('/^([0-9A-Fa-f\\x09\\x0a\\x0c\\x0d\\x20]+)>/iU', \substr($this->pdfdata, $offset), $matches) == 1) {
600 // remove white space characters
601 $objval = \strtr($matches[1], "\t\n\f\r ", '');
602 $offset += \strlen($matches[0]);
603 } elseif (($endpos = \strpos($this->pdfdata, '>', $offset)) !== \FALSE) {
604 $offset = $endpos + 1;
605 }
606 }
607 break;
608 default:
609 if (\substr($this->pdfdata, $offset, 6) == 'endobj') {
610 // indirect object
611 $objtype = 'endobj';
612 $offset += 6;
613 } elseif (\substr($this->pdfdata, $offset, 4) == 'null') {
614 // null object
615 $objtype = 'null';
616 $offset += 4;
617 $objval = 'null';
618 } elseif (\substr($this->pdfdata, $offset, 4) == 'true') {
619 // boolean true object
620 $objtype = 'boolean';
621 $offset += 4;
622 $objval = 'true';
623 } elseif (\substr($this->pdfdata, $offset, 5) == 'false') {
624 // boolean false object
625 $objtype = 'boolean';
626 $offset += 5;
627 $objval = 'false';
628 } elseif (\substr($this->pdfdata, $offset, 6) == 'stream') {
629 // start stream object
630 $objtype = 'stream';
631 $offset += 6;
632 if (\preg_match('/^([\\r]?[\\n])/isU', \substr($this->pdfdata, $offset), $matches) == 1) {
633 $offset += \strlen($matches[0]);
634 if (\preg_match('/(endstream)[\\x09\\x0a\\x0c\\x0d\\x20]/isU', \substr($this->pdfdata, $offset), $matches, \PREG_OFFSET_CAPTURE) == 1) {
635 $objval = \substr($this->pdfdata, $offset, $matches[0][1]);
636 $offset += $matches[1][1];
637 }
638 }
639 } elseif (\substr($this->pdfdata, $offset, 9) == 'endstream') {
640 // end stream object
641 $objtype = 'endstream';
642 $offset += 9;
643 } elseif (\preg_match('/^([0-9]+)[\\s]+([0-9]+)[\\s]+R/iU', \substr($this->pdfdata, $offset, 33), $matches) == 1) {
644 // indirect object reference
645 $objtype = 'objref';
646 $offset += \strlen($matches[0]);
647 $objval = \intval($matches[1]) . '_' . \intval($matches[2]);
648 } elseif (\preg_match('/^([0-9]+)[\\s]+([0-9]+)[\\s]+obj/iU', \substr($this->pdfdata, $offset, 33), $matches) == 1) {
649 // object start
650 $objtype = 'obj';
651 $objval = \intval($matches[1]) . '_' . \intval($matches[2]);
652 $offset += \strlen($matches[0]);
653 } elseif (($numlen = \strspn($this->pdfdata, '+-.0123456789', $offset)) > 0) {
654 // numeric object
655 $objtype = 'numeric';
656 $objval = \substr($this->pdfdata, $offset, $numlen);
657 $offset += $numlen;
658 }
659 break;
660 }
661 return array($objtype, $objval, $offset);
662 }
663 /**
664 * Get content of indirect object.
665 * @param string $obj_ref Object number and generation number separated by underscore character.
666 * @param int $offset Object offset.
667 * @param boolean $decoding If true decode streams.
668 * @return array containing object data.
669 * @protected
670 * @since 1.0.000 (2011-05-24)
671 */
672 protected function getIndirectObject($obj_ref, $offset = 0, $decoding = \true)
673 {
674 $obj = \explode('_', $obj_ref);
675 if ($obj === \false or \count($obj) != 2) {
676 $this->Error('Invalid object reference: ' . $obj);
677 return;
678 }
679 $objref = $obj[0] . ' ' . $obj[1] . ' obj';
680 // ignore leading zeros
681 $offset += \strspn($this->pdfdata, '0', $offset);
682 if (\strpos($this->pdfdata, $objref, $offset) != $offset) {
683 // an indirect reference to an undefined object shall be considered a reference to the null object
684 return array('null', 'null', $offset);
685 }
686 // starting position of object content
687 $offset += \strlen($objref);
688 // get array of object content
689 $objdata = array();
690 $i = 0;
691 // object main index
692 do {
693 $oldoffset = $offset;
694 // get element
695 $element = $this->getRawObject($offset);
696 $offset = $element[2];
697 // decode stream using stream's dictionary information
698 if ($decoding and $element[0] == 'stream' and isset($objdata[$i - 1][0]) and $objdata[$i - 1][0] == '<<') {
699 $element[3] = $this->decodeStream($objdata[$i - 1][1], $element[1]);
700 }
701 $objdata[$i] = $element;
702 ++$i;
703 } while ($element[0] != 'endobj' and $offset != $oldoffset);
704 // remove closing delimiter
705 \array_pop($objdata);
706 // return raw object content
707 return $objdata;
708 }
709 /**
710 * Get the content of object, resolving indect object reference if necessary.
711 * @param string $obj Object value.
712 * @return array containing object data.
713 * @protected
714 * @since 1.0.000 (2011-06-26)
715 */
716 protected function getObjectVal($obj)
717 {
718 if ($obj[0] == 'objref') {
719 // reference to indirect object
720 if (isset($this->objects[$obj[1]])) {
721 // this object has been already parsed
722 return $this->objects[$obj[1]];
723 } elseif (isset($this->xref[$obj[1]])) {
724 // parse new object
725 $this->objects[$obj[1]] = $this->getIndirectObject($obj[1], $this->xref[$obj[1]], \false);
726 return $this->objects[$obj[1]];
727 }
728 }
729 return $obj;
730 }
731 /**
732 * Decode the specified stream.
733 * @param array $sdic Stream's dictionary array.
734 * @param string $stream Stream to decode.
735 * @return array containing decoded stream data and remaining filters.
736 * @protected
737 * @since 1.0.000 (2011-06-22)
738 */
739 protected function decodeStream($sdic, $stream)
740 {
741 // get stream length and filters
742 $slength = \strlen($stream);
743 if ($slength <= 0) {
744 return array('', array());
745 }
746 $filters = array();
747 foreach ($sdic as $k => $v) {
748 if ($v[0] == '/') {
749 if ($v[1] == 'Length' and isset($sdic[$k + 1]) and $sdic[$k + 1][0] == 'numeric') {
750 // get declared stream length
751 $declength = \intval($sdic[$k + 1][1]);
752 if ($declength < $slength) {
753 $stream = \substr($stream, 0, $declength);
754 $slength = $declength;
755 }
756 } elseif ($v[1] == 'Filter' and isset($sdic[$k + 1])) {
757 // resolve indirect object
758 $objval = $this->getObjectVal($sdic[$k + 1]);
759 if ($objval[0] == '/') {
760 // single filter
761 $filters[] = $objval[1];
762 } elseif ($objval[0] == '[') {
763 // array of filters
764 foreach ($objval[1] as $flt) {
765 if ($flt[0] == '/') {
766 $filters[] = $flt[1];
767 }
768 }
769 }
770 }
771 }
772 }
773 // decode the stream
774 $remaining_filters = array();
775 foreach ($filters as $filter) {
776 if (\in_array($filter, \TCPDF_FILTERS::getAvailableFilters())) {
777 try {
778 $stream = \TCPDF_FILTERS::decodeFilter($filter, $stream);
779 } catch (\Exception $e) {
780 $emsg = $e->getMessage();
781 if ($emsg[0] == '~' and !$this->cfg['ignore_missing_filter_decoders'] or $emsg[0] != '~' and !$this->cfg['ignore_filter_decoding_errors']) {
782 $this->Error($e->getMessage());
783 }
784 }
785 } else {
786 // add missing filter to array
787 $remaining_filters[] = $filter;
788 }
789 }
790 return array($stream, $remaining_filters);
791 }
792 /**
793 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true.
794 * @param string $msg The error message
795 * @public
796 * @since 1.0.000 (2011-05-23)
797 */
798 public function Error($msg)
799 {
800 if ($this->cfg['die_for_errors']) {
801 die('<strong>TCPDF_PARSER ERROR: </strong>' . $msg);
802 } else {
803 throw new \Exception('TCPDF_PARSER ERROR: ' . $msg);
804 }
805 }
806 }
807 // END OF TCPDF_PARSER CLASS
808 //============================================================+
809 // END OF FILE
810 //============================================================+
811 }
812