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