aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/3rdparty/libraries/mpdf/mpdfi
diff options
context:
space:
mode:
authortcit <tcit@tcit.fr>2014-07-24 15:49:36 +0200
committertcit <tcit@tcit.fr>2014-07-24 15:49:36 +0200
commit4188f38ad56d7ba2ea46e94403f305243514f80c (patch)
treef357ddbd0d846ebae0ecf5d2ab00d6b7dd6eb8d5 /inc/3rdparty/libraries/mpdf/mpdfi
parent2b58426b2d4a7f1585d5d7667c0a4fbea4cd29dd (diff)
downloadwallabag-4188f38ad56d7ba2ea46e94403f305243514f80c.tar.gz
wallabag-4188f38ad56d7ba2ea46e94403f305243514f80c.tar.zst
wallabag-4188f38ad56d7ba2ea46e94403f305243514f80c.zip
add pdf and mobi libraries
Diffstat (limited to 'inc/3rdparty/libraries/mpdf/mpdfi')
-rw-r--r--inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterASCII85.php98
-rw-r--r--inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterLZW.php154
-rw-r--r--inc/3rdparty/libraries/mpdf/mpdfi/fpdi_pdf_parser.php363
-rw-r--r--inc/3rdparty/libraries/mpdf/mpdfi/pdf_context.php78
-rw-r--r--inc/3rdparty/libraries/mpdf/mpdfi/pdf_parser.php690
5 files changed, 1383 insertions, 0 deletions
diff --git a/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterASCII85.php b/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterASCII85.php
new file mode 100644
index 00000000..fc42d574
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterASCII85.php
@@ -0,0 +1,98 @@
1<?php
2//
3// FPDI - Version 1.3.1
4//
5// Copyright 2004-2009 Setasign - Jan Slabon
6//
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
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
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.
18//
19
20if (!defined('ORD_z'))
21 define('ORD_z',ord('z'));
22if (!defined('ORD_exclmark'))
23 define('ORD_exclmark', ord('!'));
24if (!defined('ORD_u'))
25 define('ORD_u', ord('u'));
26if (!defined('ORD_tilde'))
27 define('ORD_tilde', ord('~'));
28
29class FilterASCII85 {
30
31 function error($msg) {
32 die($msg);
33 }
34
35 function decode($in) {
36 $out = '';
37 $state = 0;
38 $chn = null;
39
40 $l = strlen($in);
41
42 for ($k = 0; $k < $l; ++$k) {
43 $ch = ord($in[$k]) & 0xff;
44
45 if ($ch == ORD_tilde) {
46 break;
47 }
48 if (preg_match('/^\s$/',chr($ch))) {
49 continue;
50 }
51 if ($ch == ORD_z && $state == 0) {
52 $out .= chr(0).chr(0).chr(0).chr(0);
53 continue;
54 }
55 if ($ch < ORD_exclmark || $ch > ORD_u) {
56 $this->error('Illegal character in ASCII85Decode.');
57 }
58
59 $chn[$state++] = $ch - ORD_exclmark;
60
61 if ($state == 5) {
62 $state = 0;
63 $r = 0;
64 for ($j = 0; $j < 5; ++$j)
65 $r = $r * 85 + $chn[$j];
66 $out .= chr($r >> 24);
67 $out .= chr($r >> 16);
68 $out .= chr($r >> 8);
69 $out .= chr($r);
70 }
71 }
72 $r = 0;
73
74 if ($state == 1)
75 $this->error('Illegal length in ASCII85Decode.');
76 if ($state == 2) {
77 $r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1]+1) * 85 * 85 * 85;
78 $out .= chr($r >> 24);
79 }
80 else if ($state == 3) {
81 $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2]+1) * 85 * 85;
82 $out .= chr($r >> 24);
83 $out .= chr($r >> 16);
84 }
85 else if ($state == 4) {
86 $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3]+1) * 85 ;
87 $out .= chr($r >> 24);
88 $out .= chr($r >> 16);
89 $out .= chr($r >> 8);
90 }
91
92 return $out;
93 }
94
95 function encode($in) {
96 $this->error("ASCII85 encoding not implemented.");
97 }
98} \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterLZW.php b/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterLZW.php
new file mode 100644
index 00000000..5867603f
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/mpdfi/filters/FilterLZW.php
@@ -0,0 +1,154 @@
1<?php
2//
3// FPDI - Version 1.3.1
4//
5// Copyright 2004-2009 Setasign - Jan Slabon
6//
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
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
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.
18//
19
20class FilterLZW {
21
22 var $sTable = array();
23 var $data = null;
24 var $dataLength = 0;
25 var $tIdx;
26 var $bitsToGet = 9;
27 var $bytePointer;
28 var $bitPointer;
29 var $nextData = 0;
30 var $nextBits = 0;
31 var $andTable = array(511, 1023, 2047, 4095);
32
33 function error($msg) {
34 die($msg);
35 }
36
37 /**
38 * Method to decode LZW compressed data.
39 *
40 * @param string data The compressed data.
41 */
42 function decode($data) {
43
44 if($data[0] == 0x00 && $data[1] == 0x01) {
45 $this->error('LZW flavour not supported.');
46 }
47
48 $this->initsTable();
49
50 $this->data = $data;
51 $this->dataLength = strlen($data);
52
53 // Initialize pointers
54 $this->bytePointer = 0;
55 $this->bitPointer = 0;
56
57 $this->nextData = 0;
58 $this->nextBits = 0;
59
60 $oldCode = 0;
61
62 $string = '';
63 $uncompData = '';
64
65 while (($code = $this->getNextCode()) != 257) {
66 if ($code == 256) {
67 $this->initsTable();
68 $code = $this->getNextCode();
69
70 if ($code == 257) {
71 break;
72 }
73
74 $uncompData .= $this->sTable[$code];
75 $oldCode = $code;
76
77 } else {
78
79 if ($code < $this->tIdx) {
80 $string = $this->sTable[$code];
81 $uncompData .= $string;
82
83 $this->addStringToTable($this->sTable[$oldCode], $string[0]);
84 $oldCode = $code;
85 } else {
86 $string = $this->sTable[$oldCode];
87 $string = $string.$string[0];
88 $uncompData .= $string;
89
90 $this->addStringToTable($string);
91 $oldCode = $code;
92 }
93 }
94 }
95
96 return $uncompData;
97 }
98
99
100 /**
101 * Initialize the string table.
102 */
103 function initsTable() {
104 $this->sTable = array();
105
106 for ($i = 0; $i < 256; $i++)
107 $this->sTable[$i] = chr($i);
108
109 $this->tIdx = 258;
110 $this->bitsToGet = 9;
111 }
112
113 /**
114 * Add a new string to the string table.
115 */
116 function addStringToTable ($oldString, $newString='') {
117 $string = $oldString.$newString;
118
119 // Add this new String to the table
120 $this->sTable[$this->tIdx++] = $string;
121
122 if ($this->tIdx == 511) {
123 $this->bitsToGet = 10;
124 } else if ($this->tIdx == 1023) {
125 $this->bitsToGet = 11;
126 } else if ($this->tIdx == 2047) {
127 $this->bitsToGet = 12;
128 }
129 }
130
131 // Returns the next 9, 10, 11 or 12 bits
132 function getNextCode() {
133 if ($this->bytePointer == $this->dataLength) {
134 return 257;
135 }
136
137 $this->nextData = ($this->nextData << 8) | (ord($this->data[$this->bytePointer++]) & 0xff);
138 $this->nextBits += 8;
139
140 if ($this->nextBits < $this->bitsToGet) {
141 $this->nextData = ($this->nextData << 8) | (ord($this->data[$this->bytePointer++]) & 0xff);
142 $this->nextBits += 8;
143 }
144
145 $code = ($this->nextData >> ($this->nextBits - $this->bitsToGet)) & $this->andTable[$this->bitsToGet-9];
146 $this->nextBits -= $this->bitsToGet;
147
148 return $code;
149 }
150
151 function encode($in) {
152 $this->error("LZW encoding not implemented.");
153 }
154} \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/mpdfi/fpdi_pdf_parser.php b/inc/3rdparty/libraries/mpdf/mpdfi/fpdi_pdf_parser.php
new file mode 100644
index 00000000..e0f02391
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/mpdfi/fpdi_pdf_parser.php
@@ -0,0 +1,363 @@
1<?php
2//
3// FPDI - Version 1.2
4//
5// Copyright 2004-2007 Setasign - Jan Slabon
6//
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
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
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.
18//
19
20
21class fpdi_pdf_parser extends pdf_parser {
22
23 /**
24 * Pages
25 * Index beginns at 0
26 *
27 * @var array
28 */
29 var $pages;
30
31 /**
32 * Page count
33 * @var integer
34 */
35 var $page_count;
36
37 /**
38 * actual page number
39 * @var integer
40 */
41 var $pageno;
42
43
44 /**
45 * FPDI Reference
46 * @var object
47 */
48 var $fpdi;
49
50 /**
51 * Available BoxTypes
52 *
53 * @var array
54 */
55 var $availableBoxes = array("/MediaBox","/CropBox","/BleedBox","/TrimBox","/ArtBox");
56
57 /**
58 * Constructor
59 *
60 * @param string $filename Source-Filename
61 * @param object $fpdi Object of type fpdi
62 */
63 function fpdi_pdf_parser($filename,&$fpdi) {
64 $this->fpdi =& $fpdi;
65 $this->filename = $filename;
66
67 parent::pdf_parser($filename);
68 if ($this->success == false) { return false; }
69
70 // resolve Pages-Dictonary
71 $pages = $this->pdf_resolve_object($this->c, $this->root[1][1]['/Pages']);
72 if ($this->success == false) { return false; }
73
74 // Read pages
75 $this->read_pages($this->c, $pages, $this->pages);
76 if ($this->success == false) { return false; }
77
78 // count pages;
79 $this->page_count = count($this->pages);
80 }
81
82
83 /**
84 * Get pagecount from sourcefile
85 *
86 * @return int
87 */
88 function getPageCount() {
89 return $this->page_count;
90 }
91
92
93 /**
94 * Set pageno
95 *
96 * @param int $pageno Pagenumber to use
97 */
98 function setPageno($pageno) {
99 $pageno = ((int) $pageno) - 1;
100
101 if ($pageno < 0 || $pageno >= $this->getPageCount()) {
102 $this->fpdi->error("Pagenumber is wrong!");
103 }
104
105 $this->pageno = $pageno;
106 }
107
108 /**
109 * Get page-resources from current page
110 *
111 * @return array
112 */
113 function getPageResources() {
114 return $this->_getPageResources($this->pages[$this->pageno]);
115 }
116
117 /**
118 * Get page-resources from /Page
119 *
120 * @param array $obj Array of pdf-data
121 */
122 function _getPageResources ($obj) { // $obj = /Page
123 $obj = $this->pdf_resolve_object($this->c, $obj);
124
125 // If the current object has a resources
126 // dictionary associated with it, we use
127 // it. Otherwise, we move back to its
128 // parent object.
129 if (isset ($obj[1][1]['/Resources'])) {
130 $res = $this->pdf_resolve_object($this->c, $obj[1][1]['/Resources']);
131 if ($res[0] == PDF_TYPE_OBJECT)
132 return $res[1];
133 return $res;
134 } else {
135 if (!isset ($obj[1][1]['/Parent'])) {
136 return false;
137 } else {
138 $res = $this->_getPageResources($obj[1][1]['/Parent']);
139 if ($res[0] == PDF_TYPE_OBJECT)
140 return $res[1];
141 return $res;
142 }
143 }
144 }
145
146
147 /**
148 * Get content of current page
149 *
150 * If more /Contents is an array, the streams are concated
151 *
152 * @return string
153 */
154 function getContent() {
155 $buffer = "";
156
157 if (isset($this->pages[$this->pageno][1][1]['/Contents'])) {
158 $contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']);
159 foreach($contents AS $tmp_content) {
160 $buffer .= $this->_rebuildContentStream($tmp_content).' ';
161 }
162 }
163
164 return $buffer;
165 }
166
167
168 /**
169 * Resolve all content-objects
170 *
171 * @param array $content_ref
172 * @return array
173 */
174 function _getPageContent($content_ref) {
175 $contents = array();
176
177 if ($content_ref[0] == PDF_TYPE_OBJREF) {
178 $content = $this->pdf_resolve_object($this->c, $content_ref);
179 if ($content[1][0] == PDF_TYPE_ARRAY) {
180 $contents = $this->_getPageContent($content[1]);
181 } else {
182 $contents[] = $content;
183 }
184 } else if ($content_ref[0] == PDF_TYPE_ARRAY) {
185 foreach ($content_ref[1] AS $tmp_content_ref) {
186 $contents = array_merge($contents,$this->_getPageContent($tmp_content_ref));
187 }
188 }
189
190 return $contents;
191 }
192
193
194 /**
195 * Rebuild content-streams
196 *
197 * @param array $obj
198 * @return string
199 */
200 function _rebuildContentStream($obj) {
201 $filters = array();
202
203 if (isset($obj[1][1]['/Filter'])) {
204 $_filter = $obj[1][1]['/Filter'];
205
206 if ($_filter[0] == PDF_TYPE_TOKEN) {
207 $filters[] = $_filter;
208 } else if ($_filter[0] == PDF_TYPE_ARRAY) {
209 $filters = $_filter[1];
210 }
211 }
212
213 $stream = $obj[2][1];
214
215 foreach ($filters AS $_filter) {
216 switch ($_filter[1]) {
217 case "/FlateDecode":
218 if (function_exists('gzuncompress')) {
219 $stream = (strlen($stream) > 0) ? @gzuncompress($stream) : '';
220 } else {
221 $this->fpdi->error(sprintf("To handle %s filter, please compile php with zlib support.",$_filter[1]));
222 }
223 if ($stream === false) {
224 $this->fpdi->error("Error while decompressing stream.");
225 }
226 break;
227 // mPDF 4.2.003
228 case '/LZWDecode':
229 include_once(_MPDF_PATH.'mpdfi/filters/FilterLZW.php');
230 // mPDF 5.0 Removed pass by reference =&
231 $decoder = new FilterLZW();
232 $stream = $decoder->decode($stream);
233 break;
234 case '/ASCII85Decode':
235 include_once(_MPDF_PATH.'mpdfi/filters/FilterASCII85.php');
236 // mPDF 5.0 Removed pass by reference =&
237 $decoder = new FilterASCII85();
238 $stream = $decoder->decode($stream);
239 break;
240 case null:
241 $stream = $stream;
242 break;
243 default:
244 $this->fpdi->error(sprintf("Unsupported Filter: %s",$_filter[1]));
245 }
246 }
247
248 return $stream;
249 }
250
251
252 /**
253 * Get a Box from a page
254 * Arrayformat is same as used by fpdf_tpl
255 *
256 * @param array $page a /Page
257 * @param string $box_index Type of Box @see $availableBoxes
258 * @return array
259 */
260 function getPageBox($page, $box_index) {
261 $page = $this->pdf_resolve_object($this->c,$page);
262 $box = null;
263 if (isset($page[1][1][$box_index]))
264 $box =& $page[1][1][$box_index];
265
266 if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) {
267 $tmp_box = $this->pdf_resolve_object($this->c,$box);
268 $box = $tmp_box[1];
269 }
270
271 if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) {
272 $b =& $box[1];
273 return array("x" => $b[0][1]/_MPDFK,
274 "y" => $b[1][1]/_MPDFK,
275 "w" => abs($b[0][1]-$b[2][1])/_MPDFK,
276 "h" => abs($b[1][1]-$b[3][1])/_MPDFK); // mPDF 5.3.90
277 } else if (!isset ($page[1][1]['/Parent'])) {
278 return false;
279 } else {
280 return $this->getPageBox($this->pdf_resolve_object($this->c, $page[1][1]['/Parent']), $box_index);
281 }
282 }
283
284 function getPageBoxes($pageno) {
285 return $this->_getPageBoxes($this->pages[$pageno-1]);
286 }
287
288 /**
289 * Get all Boxes from /Page
290 *
291 * @param array a /Page
292 * @return array
293 */
294 function _getPageBoxes($page) {
295 $boxes = array();
296
297 foreach($this->availableBoxes AS $box) {
298 if ($_box = $this->getPageBox($page,$box)) {
299 $boxes[$box] = $_box;
300 }
301 }
302
303 return $boxes;
304 }
305
306 function getPageRotation($pageno) {
307 return $this->_getPageRotation($this->pages[$pageno-1]);
308 }
309
310 function _getPageRotation ($obj) { // $obj = /Page
311 $obj = $this->pdf_resolve_object($this->c, $obj);
312 if (isset ($obj[1][1]['/Rotate'])) {
313 $res = $this->pdf_resolve_object($this->c, $obj[1][1]['/Rotate']);
314 if ($res[0] == PDF_TYPE_OBJECT)
315 return $res[1];
316 return $res;
317 } else {
318 if (!isset ($obj[1][1]['/Parent'])) {
319 return false;
320 } else {
321 $res = $this->_getPageRotation($obj[1][1]['/Parent']);
322 if ($res[0] == PDF_TYPE_OBJECT)
323 return $res[1];
324 return $res;
325 }
326 }
327 }
328
329 /**
330 * Read all /Page(es)
331 *
332 * @param object pdf_context
333 * @param array /Pages
334 * @param array the result-array
335 */
336 function read_pages (&$c, &$pages, &$result) {
337 // Get the kids dictionary
338 $kids = $this->pdf_resolve_object ($c, $pages[1][1]['/Kids']);
339
340 if (!is_array($kids)) {
341 // mPDF 4.0
342 $this->success = false;
343 $this->errormsg = sprintf("Cannot find /Kids in current /Page-Dictionary");
344 return false;
345 }
346 foreach ($kids[1] as $v) {
347 $pg = $this->pdf_resolve_object ($c, $v);
348 if ($pg[1][1]['/Type'][1] === '/Pages') {
349 // If one of the kids is an embedded
350 // /Pages array, resolve it as well.
351 $this->read_pages ($c, $pg, $result);
352 } else {
353 $result[] = $pg;
354 }
355 }
356 }
357
358
359
360
361}
362
363?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/mpdfi/pdf_context.php b/inc/3rdparty/libraries/mpdf/mpdfi/pdf_context.php
new file mode 100644
index 00000000..6cf4bdcc
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/mpdfi/pdf_context.php
@@ -0,0 +1,78 @@
1<?php
2//
3// FPDI - Version 1.2
4//
5// Copyright 2004-2007 Setasign - Jan Slabon
6//
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
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
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.
18//
19
20class pdf_context {
21
22 var $file;
23 var $buffer;
24 var $offset;
25 var $length;
26
27 var $stack;
28
29 // Constructor
30
31 function pdf_context($f) {
32 $this->file = $f;
33 $this->reset();
34 }
35
36 // Optionally move the file
37 // pointer to a new location
38 // and reset the buffered data
39
40 function reset($pos = null, $l = 100) {
41 if (!is_null ($pos)) {
42 fseek ($this->file, $pos);
43 }
44
45 $this->buffer = $l > 0 ? fread($this->file, $l) : '';
46 $this->offset = 0;
47 $this->length = strlen($this->buffer);
48 $this->stack = array();
49 }
50
51 // Make sure that there is at least one
52 // character beyond the current offset in
53 // the buffer to prevent the tokenizer
54 // from attempting to access data that does
55 // not exist
56
57 function ensure_content() {
58 if ($this->offset >= $this->length - 1) {
59 return $this->increase_length();
60 } else {
61 return true;
62 }
63 }
64
65 // Forcefully read more data into the buffer
66
67 function increase_length($l=100) {
68 if (feof($this->file)) {
69 return false;
70 } else {
71 $this->buffer .= fread($this->file, $l);
72 $this->length = strlen($this->buffer);
73 return true;
74 }
75 }
76
77}
78?> \ No newline at end of file
diff --git a/inc/3rdparty/libraries/mpdf/mpdfi/pdf_parser.php b/inc/3rdparty/libraries/mpdf/mpdfi/pdf_parser.php
new file mode 100644
index 00000000..c7d69e2c
--- /dev/null
+++ b/inc/3rdparty/libraries/mpdf/mpdfi/pdf_parser.php
@@ -0,0 +1,690 @@
1<?php
2//
3// FPDI - Version 1.2
4//
5// Copyright 2004-2007 Setasign - Jan Slabon
6//
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
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
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.
18//
19
20if (!defined ('PDF_TYPE_NULL'))
21 define ('PDF_TYPE_NULL', 0);
22if (!defined ('PDF_TYPE_NUMERIC'))
23 define ('PDF_TYPE_NUMERIC', 1);
24if (!defined ('PDF_TYPE_TOKEN'))
25 define ('PDF_TYPE_TOKEN', 2);
26if (!defined ('PDF_TYPE_HEX'))
27 define ('PDF_TYPE_HEX', 3);
28if (!defined ('PDF_TYPE_STRING'))
29 define ('PDF_TYPE_STRING', 4);
30if (!defined ('PDF_TYPE_DICTIONARY'))
31 define ('PDF_TYPE_DICTIONARY', 5);
32if (!defined ('PDF_TYPE_ARRAY'))
33 define ('PDF_TYPE_ARRAY', 6);
34if (!defined ('PDF_TYPE_OBJDEC'))
35 define ('PDF_TYPE_OBJDEC', 7);
36if (!defined ('PDF_TYPE_OBJREF'))
37 define ('PDF_TYPE_OBJREF', 8);
38if (!defined ('PDF_TYPE_OBJECT'))
39 define ('PDF_TYPE_OBJECT', 9);
40if (!defined ('PDF_TYPE_STREAM'))
41 define ('PDF_TYPE_STREAM', 10);
42
43
44class pdf_parser {
45
46 /**
47 * Filename
48 * @var string
49 */
50 var $filename;
51
52 /**
53 * File resource
54 * @var resource
55 */
56 var $f;
57
58 /**
59 * PDF Context
60 * @var object pdf_context-Instance
61 */
62 var $c;
63
64 /**
65 * xref-Data
66 * @var array
67 */
68 var $xref;
69
70 /**
71 * root-Object
72 * @var array
73 */
74 var $root;
75
76 // mPDF 4.0 Added flag to show success on loading file
77 var $success;
78 var $errormsg;
79
80 /**
81 * Constructor
82 *
83 * @param string $filename Source-Filename
84 */
85 function pdf_parser($filename) {
86 $this->filename = $filename;
87 // mPDF 4.0
88 $this->success = true;
89
90 $this->f = @fopen($this->filename, "rb");
91
92 if (!$this->f) {
93 $this->success = false;
94 $this->errormsg = sprintf("Cannot open %s !", $filename);
95 return false;
96 }
97 // mPDF 5.0 Removed pass by reference =&
98 $this->c = new pdf_context($this->f);
99 // Read xref-Data
100 $offset = $this->pdf_find_xref();
101 if ($offset===false) {
102 $this->success = false;
103 $this->errormsg = sprintf("Cannot open %s !", $filename);
104 return false;
105 }
106 $this->pdf_read_xref($this->xref, $offset);
107 if ($this->success == false) { return false; }
108
109 // Check for Encryption
110 $this->getEncryption();
111 if ($this->success == false) { return false; }
112
113 // Read root
114 $this->pdf_read_root();
115 if ($this->success == false) { return false; }
116 }
117
118 /**
119 * Close the opened file
120 */
121 function closeFile() {
122 if (isset($this->f)) {
123 fclose($this->f);
124 unset($this->f);
125 }
126 }
127
128 /**
129 * Print Error and die
130 *
131 * @param string $msg Error-Message
132 */
133 function error($msg) {
134 die("<b>PDF-Parser Error:</b> ".$msg);
135 }
136
137 /**
138 * Check Trailer for Encryption
139 */
140 function getEncryption() {
141 if (isset($this->xref['trailer'][1]['/Encrypt'])) {
142 // mPDF 4.0
143 $this->success = false;
144 $this->errormsg = sprintf("File is encrypted!");
145 return false;
146 }
147 }
148
149 /**
150 * Find/Return /Root
151 *
152 * @return array
153 */
154 function pdf_find_root() {
155 if ($this->xref['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF) {
156 // mPDF 4.0
157 $this->success = false;
158 $this->errormsg = sprintf("Wrong Type of Root-Element! Must be an indirect reference");
159 return false;
160 }
161 return $this->xref['trailer'][1]['/Root'];
162 }
163
164 /**
165 * Read the /Root
166 */
167 function pdf_read_root() {
168 // read root
169 $root = $this->pdf_find_root();
170 if ($root ===false) {
171 $this->success = false;
172 return false;
173 }
174 $this->root = $this->pdf_resolve_object($this->c, $root);
175 }
176
177 /**
178 * Find the xref-Table
179 */
180 function pdf_find_xref() {
181 fseek ($this->f, -min(filesize($this->filename),1500), SEEK_END);
182 $data = fread($this->f, 1500);
183
184 $pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
185 $data = substr($data, $pos);
186
187 if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
188 // mPDF 4.0
189 $this->success = false;
190 $this->errormsg = sprintf("Unable to find pointer to xref table");
191 return false;
192 }
193
194 return (int) $matches[1];
195 }
196
197 /**
198 * Read xref-table
199 *
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
204 */
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));
209
210 if (strlen($data) == 0)
211 $data = trim(fgets($this->f,1024));
212
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);
222 return;
223 } else {
224 // mPDF 4.0
225 $this->success = false;
226 $this->errormsg = sprintf("Unable to find xref table - Maybe a Problem with 'auto_detect_line_endings'");
227 return;
228 }
229 }
230 }
231
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)));
237
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);
242 } else {
243 // mPDF 4.0
244 $this->success = false;
245 $this->errormsg = sprintf("Unexpected header in xref table");
246 return;
247 }
248 }
249 }
250 $start = $data[0];
251 $end = $start + $data[1];
252 }
253
254 if (!isset($result['xref_location'])) {
255 $result['xref_location'] = $offset;
256 }
257
258 if (!isset($result['max_object']) || $end > $result['max_object']) {
259 $result['max_object'] = $end;
260 }
261
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);
266
267 if (!isset ($result['xref'][$start][(int) $generation])) {
268 $result['xref'][$start][(int) $generation] = (int) $offset;
269 }
270 }
271
272 $o_pos = ftell($this->f);
273 $data = fgets($this->f,1024);
274 if (strlen(trim($data)) == 0)
275 $data = fgets($this->f, 1024);
276
277 if (preg_match("/trailer/",$data)) {
278 if (preg_match("/(.*trailer[ \n\r]*)/",$data,$m)) {
279 fseek($this->f, $o_pos+strlen($m[1]));
280 }
281
282 // mPDF 5.0 Removed pass by reference =&
283 $c = new pdf_context($this->f);
284 $trailer = $this->pdf_read_value($c);
285
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]);
289 } else {
290 $result['trailer'] = $trailer;
291 }
292 } else {
293 $data = explode(' ', trim($data));
294
295 if (count($data) != 2) {
296 fseek($this->f, $o_pos);
297 $data = explode(' ', trim (_fgets ($this->f, true)));
298
299 if (count($data) != 2) {
300 // mPDF 4.0
301 $this->success = false;
302 $this->errormsg = sprintf("Unexpected data in xref table");
303 return;
304 }
305 }
306
307 $this->pdf_read_xref($result, null, (int) $data[0], (int) $data[0] + (int) $data[1]);
308 }
309 }
310
311
312 /**
313 * Reads an Value
314 *
315 * @param object $c pdf_context
316 * @param string $token a Token
317 * @return mixed
318 */
319 function pdf_read_value(&$c, $token = null) {
320 if (is_null($token)) {
321 $token = $this->pdf_read_token($c);
322 }
323
324 if ($token === false) {
325 return false;
326 }
327
328 switch ($token) {
329 case '<':
330 // This is a hex string.
331 // Read the value, then the terminator
332
333 $pos = $c->offset;
334
335 while(1) {
336
337 $match = strpos ($c->buffer, '>', $pos);
338
339 // If you can't find it, try
340 // reading more data from the stream
341
342 if ($match === false) {
343 if (!$c->increase_length()) {
344 return false;
345 } else {
346 continue;
347 }
348 }
349
350 $result = substr ($c->buffer, $c->offset, $match - $c->offset);
351 $c->offset = $match+1;
352
353 return array (PDF_TYPE_HEX, $result);
354 }
355
356 break;
357 case '<<':
358 // This is a dictionary.
359
360 $result = array();
361
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) {
366 return false;
367 }
368
369 if (($value = $this->pdf_read_value($c)) === false) {
370 return false;
371 }
372 $result[$key] = $value;
373 }
374
375 return array (PDF_TYPE_DICTIONARY, $result);
376
377 case '[':
378 // This is an array.
379
380 $result = array();
381
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) {
386 return false;
387 }
388
389 if (($value = $this->pdf_read_value($c, $token)) === false) {
390 return false;
391 }
392
393 $result[] = $value;
394 }
395
396 return array (PDF_TYPE_ARRAY, $result);
397
398 case '(' :
399 // This is a string
400
401 $pos = $c->offset;
402
403 while(1) {
404
405 // Start by finding the next closed
406 // parenthesis
407
408 $match = strpos ($c->buffer, ')', $pos);
409
410 // If you can't find it, try
411 // reading more data from the stream
412
413 if ($match === false) {
414 if (!$c->increase_length()) {
415 return false;
416 } else {
417 continue;
418 }
419 }
420
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);
425
426 if ($esc === 0 || strlen($m[1]) % 2 == 0) {
427 $result = $tmpresult;
428 $c->offset = $match + 1;
429 return array (PDF_TYPE_STRING, $result);
430 } else {
431 $pos = $match + 1;
432
433 if ($pos > $c->offset + $c->length) {
434 $c->increase_length();
435 }
436 }
437 }
438
439 case "stream":
440 $o_pos = ftell($c->file)-strlen($c->buffer);
441 $o_offset = $c->offset;
442
443 $c->reset($startpos = $o_pos + $o_offset);
444
445 $e = 0; // ensure line breaks in front of the stream
446 if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
447 $e++;
448 if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
449 $e++;
450
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];
456 } else {
457 $length = $this->actual_obj[1][1]['/Length'][1];
458 }
459
460 if ($length > 0) {
461 $c->reset($startpos+$e,$length);
462 $v = $c->buffer;
463 } else {
464 $v = '';
465 }
466 $c->reset($startpos+$e+$length+9); // 9 = strlen("endstream")
467
468 return array(PDF_TYPE_STREAM, $v);
469
470 default :
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)) {
476
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) {
483 switch ($tok3) {
484 case 'obj' :
485 return array (PDF_TYPE_OBJDEC, (int) $token, (int) $tok2);
486 case 'R' :
487 return array (PDF_TYPE_OBJREF, (int) $token, (int) $tok2);
488 }
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);
493 }
494 }
495
496 array_push ($c->stack, $tok2);
497 }
498
499 return array (PDF_TYPE_NUMERIC, $token);
500 } else {
501
502 // Just a token. Return it.
503 return array (PDF_TYPE_TOKEN, $token);
504 }
505
506 }
507 }
508
509 /**
510 * Resolve an object
511 *
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
515 */
516 function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
517 // Exit if we get invalid data
518 if (!is_array($obj_spec)) {
519 return false;
520 }
521
522 if ($obj_spec[0] == PDF_TYPE_OBJREF) {
523
524 // This is a reference, resolve it
525 if (isset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]])) {
526
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
531 // of a stream)
532
533 $old_pos = ftell($c->file);
534
535 // Reposition the file pointer and
536 // load the object header.
537
538 $c->reset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]]);
539
540 $header = $this->pdf_read_value($c,null,true);
541
542 if ($header[0] != PDF_TYPE_OBJDEC || $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
543 // mPDF 4.0
544 $this->success = false;
545 $this->errormsg = sprintf("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
546 return false;
547 }
548
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;
553 if ($encapsulate) {
554 $result = array (
555 PDF_TYPE_OBJECT,
556 'obj' => $obj_spec[1],
557 'gen' => $obj_spec[2]
558 );
559 } else {
560 $result = array();
561 }
562
563 // Now simply read the object data until
564 // we encounter an end-of-object marker
565 while(1) {
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
569 break;
570 }
571
572 if ($value[0] == PDF_TYPE_TOKEN && $value[1] === 'endobj') {
573 break;
574 }
575
576 $result[] = $value;
577 }
578
579 $c->reset($old_pos);
580
581 if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM) {
582 $result[0] = PDF_TYPE_STREAM;
583 }
584
585 return $result;
586 }
587 } else {
588 return $obj_spec;
589 }
590 }
591
592
593
594 /**
595 * Reads a token from the file
596 *
597 * @param object $c pdf_context
598 * @return mixed
599 */
600 function pdf_read_token(&$c)
601 {
602 // If there is a token available
603 // on the stack, pop it out and
604 // return it.
605
606 if (count($c->stack)) {
607 return array_pop($c->stack);
608 }
609
610 // Strip away any whitespace
611
612 do {
613 if (!$c->ensure_content()) {
614 return false;
615 }
616 $c->offset += _strspn($c->buffer, " \n\r\t", $c->offset);
617 } while ($c->offset >= $c->length - 1);
618
619 // Get the first character in the stream
620
621 $char = $c->buffer[$c->offset++];
622
623 switch ($char) {
624
625 case '[' :
626 case ']' :
627 case '(' :
628 case ')' :
629
630 // This is either an array or literal string
631 // delimiter, Return it
632
633 return $char;
634
635 case '<' :
636 case '>' :
637
638 // This could either be a hex string or
639 // dictionary delimiter. Determine the
640 // appropriate case and return the token
641
642 if ($c->buffer[$c->offset] == $char) {
643 if (!$c->ensure_content()) {
644 return false;
645 }
646 $c->offset++;
647 return $char . $char;
648 } else {
649 return $char;
650 }
651
652 default :
653
654 // This is "another" type of token (probably
655 // a dictionary entry or a numeric value)
656 // Find the end and return it.
657
658 if (!$c->ensure_content()) {
659 return false;
660 }
661
662 while(1) {
663
664 // Determine the length of the token
665
666 $pos = _strcspn($c->buffer, " []<>()\r\n\t/", $c->offset);
667 if ($c->offset + $pos <= $c->length - 1) {
668 break;
669 } else {
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.
675
676 $c->increase_length();
677 }
678 }
679
680 $result = substr($c->buffer, $c->offset - 1, $pos + 1);
681
682 $c->offset += $pos;
683 return $result;
684 }
685 }
686
687
688}
689
690?> \ No newline at end of file