]>
git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/mpdf/mpdfi/pdf_parser.php
5 // Copyright 2004-2007 Setasign - Jan Slabon
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
20 if (!defined ('PDF_TYPE_NULL'))
21 define ('PDF_TYPE_NULL', 0);
22 if (!defined ('PDF_TYPE_NUMERIC'))
23 define ('PDF_TYPE_NUMERIC', 1);
24 if (!defined ('PDF_TYPE_TOKEN'))
25 define ('PDF_TYPE_TOKEN', 2);
26 if (!defined ('PDF_TYPE_HEX'))
27 define ('PDF_TYPE_HEX', 3);
28 if (!defined ('PDF_TYPE_STRING'))
29 define ('PDF_TYPE_STRING', 4);
30 if (!defined ('PDF_TYPE_DICTIONARY'))
31 define ('PDF_TYPE_DICTIONARY', 5);
32 if (!defined ('PDF_TYPE_ARRAY'))
33 define ('PDF_TYPE_ARRAY', 6);
34 if (!defined ('PDF_TYPE_OBJDEC'))
35 define ('PDF_TYPE_OBJDEC', 7);
36 if (!defined ('PDF_TYPE_OBJREF'))
37 define ('PDF_TYPE_OBJREF', 8);
38 if (!defined ('PDF_TYPE_OBJECT'))
39 define ('PDF_TYPE_OBJECT', 9);
40 if (!defined ('PDF_TYPE_STREAM'))
41 define ('PDF_TYPE_STREAM', 10);
60 * @var object pdf_context-Instance
76 // mPDF 4.0 Added flag to show success on loading file
83 * @param string $filename Source-Filename
85 function pdf_parser($filename) {
86 $this->filename
= $filename;
88 $this->success
= true;
90 $this->f
= @fopen($this->filename
, "rb");
93 $this->success
= false;
94 $this->errormsg
= sprintf("Cannot open %s !", $filename);
97 // mPDF 5.0 Removed pass by reference =&
98 $this->c
= new pdf_context($this->f
);
100 $offset = $this->pdf_find_xref();
101 if ($offset===false) {
102 $this->success
= false;
103 $this->errormsg
= sprintf("Cannot open %s !", $filename);
106 $this->pdf_read_xref($this->xref
, $offset);
107 if ($this->success
== false) { return false; }
109 // Check for Encryption
110 $this->getEncryption();
111 if ($this->success
== false) { return false; }
114 $this->pdf_read_root();
115 if ($this->success
== false) { return false; }
119 * Close the opened file
121 function closeFile() {
122 if (isset($this->f
)) {
129 * Print Error and die
131 * @param string $msg Error-Message
133 function error($msg) {
134 die("<b>PDF-Parser Error:</b> ".$msg);
138 * Check Trailer for Encryption
140 function getEncryption() {
141 if (isset($this->xref
['trailer'][1]['/Encrypt'])) {
143 $this->success
= false;
144 $this->errormsg
= sprintf("File is encrypted!");
154 function pdf_find_root() {
155 if ($this->xref
['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF
) {
157 $this->success
= false;
158 $this->errormsg
= sprintf("Wrong Type of Root-Element! Must be an indirect reference");
161 return $this->xref
['trailer'][1]['/Root'];
167 function pdf_read_root() {
169 $root = $this->pdf_find_root();
170 if ($root ===false) {
171 $this->success
= false;
174 $this->root
= $this->pdf_resolve_object($this->c
, $root);
178 * Find the xref-Table
180 function pdf_find_xref() {
181 fseek ($this->f
, -min(filesize($this->filename
),1500), SEEK_END
);
182 $data = fread($this->f
, 1500);
184 $pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
185 $data = substr($data, $pos);
187 if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
189 $this->success
= false;
190 $this->errormsg
= sprintf("Unable to find pointer to xref table");
194 return (int) $matches[1];
200 * @param array $result Array of xref-table
201 * @param integer $offset of xref-table
202 * @param integer $start start-position in xref-table
203 * @param integer $end end-position in xref-table
205 function pdf_read_xref(&$result, $offset, $start = null, $end = null) {
206 if (is_null ($start) || is_null ($end)) {
207 fseek($this->f
, $o_pos = $offset);
208 $data = trim(fgets($this->f
,1024));
210 if (strlen($data) == 0)
211 $data = trim(fgets($this->f
,1024));
213 if ($data !== 'xref') {
214 fseek($this->f
, $o_pos);
215 $data = trim(_fgets($this->f
, true));
216 if ($data !== 'xref') {
217 if (preg_match('/(.*xref)(.*)/m', $data, $m)) { // xref 0 128 - in one line
218 fseek($this->f
, $o_pos+
strlen($m[1]));
219 } elseif (preg_match('/(x|r|e|f)+/', $data, $m)) { // correct invalid xref-pointer
220 $tmpOffset = $offset-4+
strlen($m[0]);
221 $this->pdf_read_xref($result, $tmpOffset, $start, $end);
225 $this->success
= false;
226 $this->errormsg
= sprintf("Unable to find xref table - Maybe a Problem with 'auto_detect_line_endings'");
232 $o_pos = ftell($this->f
);
233 $data = explode(' ', trim(fgets($this->f
,1024)));
234 if (count($data) != 2) {
235 fseek($this->f
, $o_pos);
236 $data = explode(' ', trim(_fgets($this->f
, true)));
238 if (count($data) != 2) {
239 if (count($data) > 2) { // no lineending
240 $n_pos = $o_pos+
strlen($data[0])+
strlen($data[1])+
2;
241 fseek($this->f
, $n_pos);
244 $this->success
= false;
245 $this->errormsg
= sprintf("Unexpected header in xref table");
251 $end = $start +
$data[1];
254 if (!isset($result['xref_location'])) {
255 $result['xref_location'] = $offset;
258 if (!isset($result['max_object']) || $end > $result['max_object']) {
259 $result['max_object'] = $end;
262 for (; $start < $end; $start++
) {
263 $data = ltrim(fread($this->f
, 20)); // Spezifications says: 20 bytes including newlines
264 $offset = substr($data, 0, 10);
265 $generation = substr($data, 11, 5);
267 if (!isset ($result['xref'][$start][(int) $generation])) {
268 $result['xref'][$start][(int) $generation] = (int) $offset;
272 $o_pos = ftell($this->f
);
273 $data = fgets($this->f
,1024);
274 if (strlen(trim($data)) == 0)
275 $data = fgets($this->f
, 1024);
277 if (preg_match("/trailer/",$data)) {
278 if (preg_match("/(.*trailer[ \n\r]*)/",$data,$m)) {
279 fseek($this->f
, $o_pos+
strlen($m[1]));
282 // mPDF 5.0 Removed pass by reference =&
283 $c = new pdf_context($this->f
);
284 $trailer = $this->pdf_read_value($c);
286 if (isset($trailer[1]['/Prev'])) {
287 $this->pdf_read_xref($result, $trailer[1]['/Prev'][1]);
288 $result['trailer'][1] = array_merge($result['trailer'][1], $trailer[1]);
290 $result['trailer'] = $trailer;
293 $data = explode(' ', trim($data));
295 if (count($data) != 2) {
296 fseek($this->f
, $o_pos);
297 $data = explode(' ', trim (_fgets ($this->f
, true)));
299 if (count($data) != 2) {
301 $this->success
= false;
302 $this->errormsg
= sprintf("Unexpected data in xref table");
307 $this->pdf_read_xref($result, null, (int) $data[0], (int) $data[0] +
(int) $data[1]);
315 * @param object $c pdf_context
316 * @param string $token a Token
319 function pdf_read_value(&$c, $token = null) {
320 if (is_null($token)) {
321 $token = $this->pdf_read_token($c);
324 if ($token === false) {
330 // This is a hex string.
331 // Read the value, then the terminator
337 $match = strpos ($c->buffer
, '>', $pos);
339 // If you can't find it, try
340 // reading more data from the stream
342 if ($match === false) {
343 if (!$c->increase_length()) {
350 $result = substr ($c->buffer
, $c->offset
, $match - $c->offset
);
351 $c->offset
= $match+
1;
353 return array (PDF_TYPE_HEX
, $result);
358 // This is a dictionary.
362 // Recurse into this function until we reach
363 // the end of the dictionary.
364 while (($key = $this->pdf_read_token($c)) !== '>>') {
365 if ($key === false) {
369 if (($value = $this->pdf_read_value($c)) === false) {
372 $result[$key] = $value;
375 return array (PDF_TYPE_DICTIONARY
, $result);
382 // Recurse into this function until we reach
383 // the end of the array.
384 while (($token = $this->pdf_read_token($c)) !== ']') {
385 if ($token === false) {
389 if (($value = $this->pdf_read_value($c, $token)) === false) {
396 return array (PDF_TYPE_ARRAY
, $result);
405 // Start by finding the next closed
408 $match = strpos ($c->buffer
, ')', $pos);
410 // If you can't find it, try
411 // reading more data from the stream
413 if ($match === false) {
414 if (!$c->increase_length()) {
421 // Make sure that there is no backslash
422 // before the parenthesis. If there is,
423 // move on. Otherwise, return the string.
424 $esc = preg_match('/([\\\\]+)$/', $tmpresult = substr($c->buffer
, $c->offset
, $match - $c->offset
), $m);
426 if ($esc === 0 || strlen($m[1]) %
2 == 0) {
427 $result = $tmpresult;
428 $c->offset
= $match +
1;
429 return array (PDF_TYPE_STRING
, $result);
433 if ($pos > $c->offset +
$c->length
) {
434 $c->increase_length();
440 $o_pos = ftell($c->file
)-strlen($c->buffer
);
441 $o_offset = $c->offset
;
443 $c->reset($startpos = $o_pos +
$o_offset);
445 $e = 0; // ensure line breaks in front of the stream
446 if ($c->buffer
[0] == chr(10) || $c->buffer
[0] == chr(13))
448 if ($c->buffer
[1] == chr(10) && $c->buffer
[0] != chr(10))
451 if ($this->actual_obj
[1][1]['/Length'][0] == PDF_TYPE_OBJREF
) {
452 // mPDF 5.0 Removed pass by reference =&
453 $tmp_c = new pdf_context($this->f
);
454 $tmp_length = $this->pdf_resolve_object($tmp_c,$this->actual_obj
[1][1]['/Length']);
455 $length = $tmp_length[1][1];
457 $length = $this->actual_obj
[1][1]['/Length'][1];
461 $c->reset($startpos+
$e,$length);
466 $c->reset($startpos+
$e+
$length+
9); // 9 = strlen("endstream")
468 return array(PDF_TYPE_STREAM
, $v);
471 if (is_numeric ($token)) {
472 // A numeric token. Make sure that
473 // it is not part of something else.
474 if (($tok2 = $this->pdf_read_token ($c)) !== false) {
475 if (is_numeric ($tok2)) {
477 // Two numeric tokens in a row.
478 // In this case, we're probably in
479 // front of either an object reference
480 // or an object specification.
481 // Determine the case and return the data
482 if (($tok3 = $this->pdf_read_token ($c)) !== false) {
485 return array (PDF_TYPE_OBJDEC
, (int) $token, (int) $tok2);
487 return array (PDF_TYPE_OBJREF
, (int) $token, (int) $tok2);
489 // If we get to this point, that numeric value up
490 // there was just a numeric value. Push the extra
491 // tokens back into the stack and return the value.
492 array_push ($c->stack
, $tok3);
496 array_push ($c->stack
, $tok2);
499 return array (PDF_TYPE_NUMERIC
, $token);
502 // Just a token. Return it.
503 return array (PDF_TYPE_TOKEN
, $token);
512 * @param object $c pdf_context
513 * @param array $obj_spec The object-data
514 * @param boolean $encapsulate Must set to true, cause the parsing and fpdi use this method only without this para
516 function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
517 // Exit if we get invalid data
518 if (!is_array($obj_spec)) {
522 if ($obj_spec[0] == PDF_TYPE_OBJREF
) {
524 // This is a reference, resolve it
525 if (isset($this->xref
['xref'][$obj_spec[1]][$obj_spec[2]])) {
527 // Save current file position
528 // This is needed if you want to resolve
529 // references while you're reading another object
530 // (e.g.: if you need to determine the length
533 $old_pos = ftell($c->file
);
535 // Reposition the file pointer and
536 // load the object header.
538 $c->reset($this->xref
['xref'][$obj_spec[1]][$obj_spec[2]]);
540 $header = $this->pdf_read_value($c,null,true);
542 if ($header[0] != PDF_TYPE_OBJDEC
|| $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
544 $this->success
= false;
545 $this->errormsg
= sprintf("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
549 // If we're being asked to store all the information
550 // about the object, we add the object ID and generation
551 // number for later use
552 $this->actual_obj
=& $result;
556 'obj' => $obj_spec[1],
557 'gen' => $obj_spec[2]
563 // Now simply read the object data until
564 // we encounter an end-of-object marker
566 $value = $this->pdf_read_value($c);
567 if ($value === false || count($result) > 4) {
568 // in this case the parser coudn't find an endobj so we break here
572 if ($value[0] == PDF_TYPE_TOKEN
&& $value[1] === 'endobj') {
581 if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM
) {
582 $result[0] = PDF_TYPE_STREAM
;
595 * Reads a token from the file
597 * @param object $c pdf_context
600 function pdf_read_token(&$c)
602 // If there is a token available
603 // on the stack, pop it out and
606 if (count($c->stack
)) {
607 return array_pop($c->stack
);
610 // Strip away any whitespace
613 if (!$c->ensure_content()) {
616 $c->offset +
= _strspn($c->buffer
, " \n\r\t", $c->offset
);
617 } while ($c->offset
>= $c->length
- 1);
619 // Get the first character in the stream
621 $char = $c->buffer
[$c->offset++
];
630 // This is either an array or literal string
631 // delimiter, Return it
638 // This could either be a hex string or
639 // dictionary delimiter. Determine the
640 // appropriate case and return the token
642 if ($c->buffer
[$c->offset
] == $char) {
643 if (!$c->ensure_content()) {
647 return $char . $char;
654 // This is "another" type of token (probably
655 // a dictionary entry or a numeric value)
656 // Find the end and return it.
658 if (!$c->ensure_content()) {
664 // Determine the length of the token
666 $pos = _strcspn($c->buffer
, " []<>()\r\n\t/", $c->offset
);
667 if ($c->offset +
$pos <= $c->length
- 1) {
670 // If the script reaches this point,
671 // the token may span beyond the end
672 // of the current buffer. Therefore,
673 // we increase the size of the buffer
674 // and try again--just to be safe.
676 $c->increase_length();
680 $result = substr($c->buffer
, $c->offset
- 1, $pos +
1);