1
|
|
|
<?php
|
|
|
|
|
2
|
|
|
//
|
3
|
|
|
// FPDI - Version 1.2.1
|
4
|
|
|
//
|
5
|
|
|
// Copyright 2004-2008 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
|
|
|
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);
|
42
|
|
|
if (!defined ('PDF_TYPE_BOOLEAN'))
|
43
|
|
|
define ('PDF_TYPE_BOOLEAN', 11);
|
44
|
|
|
if (!defined ('PDF_TYPE_REAL'))
|
45
|
|
|
define ('PDF_TYPE_REAL', 12);
|
46
|
|
|
|
47
|
|
|
require_once("pdf_context.php");
|
48
|
|
|
require_once("wrapper_functions.php");
|
49
|
|
|
|
50
|
|
|
class pdf_parser {
|
|
|
|
|
51
|
|
|
|
52
|
|
|
/**
|
53
|
|
|
* Filename
|
54
|
|
|
* @var string
|
55
|
|
|
*/
|
56
|
|
|
var $filename;
|
|
|
|
|
57
|
|
|
|
58
|
|
|
/**
|
59
|
|
|
* File resource
|
60
|
|
|
* @var resource
|
61
|
|
|
*/
|
62
|
|
|
var $f;
|
|
|
|
|
63
|
|
|
|
64
|
|
|
/**
|
65
|
|
|
* PDF Context
|
66
|
|
|
* @var object pdf_context-Instance
|
67
|
|
|
*/
|
68
|
|
|
var $c;
|
|
|
|
|
69
|
|
|
|
70
|
|
|
/**
|
71
|
|
|
* xref-Data
|
72
|
|
|
* @var array
|
73
|
|
|
*/
|
74
|
|
|
var $xref;
|
|
|
|
|
75
|
|
|
|
76
|
|
|
/**
|
77
|
|
|
* root-Object
|
78
|
|
|
* @var array
|
79
|
|
|
*/
|
80
|
|
|
var $root;
|
|
|
|
|
81
|
|
|
|
82
|
|
|
/**
|
83
|
|
|
* PDF version of the loaded document
|
84
|
|
|
* @var string
|
85
|
|
|
*/
|
86
|
|
|
var $pdfVersion;
|
|
|
|
|
87
|
|
|
|
88
|
|
|
/**
|
89
|
|
|
* Constructor
|
90
|
|
|
*
|
91
|
|
|
* @param string $filename Source-Filename
|
92
|
|
|
*/
|
93
|
|
|
function pdf_parser($filename) {
|
|
|
|
|
94
|
|
|
$this->filename = $filename;
|
95
|
|
|
|
96
|
|
|
$this->f = @fopen($this->filename, "rb");
|
97
|
|
|
|
98
|
|
|
if (!$this->f)
|
99
|
|
|
$this->error(sprintf("Cannot open %s !", $filename));
|
100
|
|
|
|
101
|
|
|
$this->getPDFVersion();
|
102
|
|
|
|
103
|
|
|
$this->c =& new pdf_context($this->f);
|
104
|
|
|
// Read xref-Data
|
105
|
|
|
$this->pdf_read_xref($this->xref, $this->pdf_find_xref());
|
106
|
|
|
|
107
|
|
|
// Check for Encryption
|
108
|
|
|
$this->getEncryption();
|
109
|
|
|
|
110
|
|
|
// Read root
|
111
|
|
|
$this->pdf_read_root();
|
112
|
|
|
}
|
113
|
|
|
|
114
|
|
|
/**
|
115
|
|
|
* Close the opened file
|
116
|
|
|
*/
|
117
|
|
|
function closeFile() {
|
|
|
|
|
118
|
|
|
if (isset($this->f)) {
|
119
|
|
|
fclose($this->f);
|
120
|
|
|
unset($this->f);
|
121
|
|
|
}
|
122
|
|
|
}
|
123
|
|
|
|
124
|
|
|
/**
|
125
|
|
|
* Print Error and die
|
126
|
|
|
*
|
127
|
|
|
* @param string $msg Error-Message
|
128
|
|
|
*/
|
129
|
|
|
function error($msg) {
|
|
|
|
|
130
|
|
|
die("<b>PDF-Parser Error:</b> ".$msg);
|
|
|
|
|
131
|
|
|
}
|
132
|
|
|
|
133
|
|
|
/**
|
134
|
|
|
* Check Trailer for Encryption
|
135
|
|
|
*/
|
136
|
|
|
function getEncryption() {
|
|
|
|
|
137
|
|
|
if (isset($this->xref['trailer'][1]['/Encrypt'])) {
|
138
|
|
|
$this->error("File is encrypted!");
|
139
|
|
|
}
|
140
|
|
|
}
|
141
|
|
|
|
142
|
|
|
/**
|
143
|
|
|
* Find/Return /Root
|
144
|
|
|
*
|
145
|
|
|
* @return array
|
146
|
|
|
*/
|
147
|
|
|
function pdf_find_root() {
|
|
|
|
|
148
|
|
|
if ($this->xref['trailer'][1]['/Root'][0] != PDF_TYPE_OBJREF) {
|
149
|
|
|
$this->error("Wrong Type of Root-Element! Must be an indirect reference");
|
150
|
|
|
}
|
151
|
|
|
return $this->xref['trailer'][1]['/Root'];
|
152
|
|
|
}
|
153
|
|
|
|
154
|
|
|
/**
|
155
|
|
|
* Read the /Root
|
156
|
|
|
*/
|
157
|
|
|
function pdf_read_root() {
|
|
|
|
|
158
|
|
|
// read root
|
159
|
|
|
$this->root = $this->pdf_resolve_object($this->c, $this->pdf_find_root());
|
|
|
|
|
160
|
|
|
}
|
161
|
|
|
|
162
|
|
|
/**
|
163
|
|
|
* Get PDF-Version
|
164
|
|
|
*
|
165
|
|
|
* And reset the PDF Version used in FPDI if needed
|
166
|
|
|
*/
|
167
|
|
|
function getPDFVersion() {
|
|
|
|
|
168
|
|
|
fseek($this->f, 0);
|
169
|
|
|
preg_match("/\d\.\d/",fread($this->f,16),$m);
|
170
|
|
|
if (isset($m[0]))
|
171
|
|
|
$this->pdfVersion = $m[0];
|
172
|
|
|
return $this->pdfVersion;
|
173
|
|
|
}
|
174
|
|
|
|
175
|
|
|
/**
|
176
|
|
|
* Find the xref-Table
|
177
|
|
|
*/
|
178
|
|
|
function pdf_find_xref() {
|
|
|
|
|
179
|
|
|
$toRead = 1500;
|
180
|
|
|
|
181
|
|
|
$stat = fseek ($this->f, -$toRead, SEEK_END);
|
182
|
|
|
if ($stat === -1) {
|
183
|
|
|
fseek ($this->f, 0);
|
184
|
|
|
}
|
185
|
|
|
$data = fread($this->f, $toRead);
|
186
|
|
|
|
187
|
|
|
$pos = strlen($data) - strpos(strrev($data), strrev('startxref'));
|
188
|
|
|
$data = substr($data, $pos);
|
189
|
|
|
|
190
|
|
|
if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) {
|
191
|
|
|
$this->error("Unable to find pointer to xref table");
|
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
|
|
|
*/
|
203
|
|
|
function pdf_read_xref(&$result, $offset) {
|
|
|
|
|
204
|
|
|
fseek($this->f, $o_pos = $offset-20); // set some bytes backwards to fetch errorious docs
|
205
|
|
|
|
206
|
|
|
$data = fread($this->f, 100);
|
207
|
|
|
|
208
|
|
|
$xrefPos = strpos($data, 'xref');
|
209
|
|
|
|
210
|
|
|
if ($xrefPos === false) {
|
211
|
|
|
$this->error('Unable to find xref table.');
|
212
|
|
|
}
|
213
|
|
|
|
214
|
|
|
if (!isset($result['xref_location'])) {
|
215
|
|
|
$result['xref_location'] = $o_pos+$xrefPos;
|
216
|
|
|
$result['max_object'] = 0;
|
217
|
|
|
}
|
218
|
|
|
|
219
|
|
|
$cylces = -1;
|
220
|
|
|
$bytesPerCycle = 100;
|
221
|
|
|
|
222
|
|
|
fseek($this->f, $o_pos = $o_pos+$xrefPos+4); // set the handle directly after the "xref"-keyword
|
223
|
|
|
$data = fread($this->f, $bytesPerCycle);
|
224
|
|
|
|
225
|
|
|
while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle*$cylces++, 0))) === false && !feof($this->f)) {
|
226
|
|
|
$data .= fread($this->f, $bytesPerCycle);
|
227
|
|
|
}
|
228
|
|
|
|
229
|
|
|
if ($trailerPos === false) {
|
230
|
|
|
$this->error('Trailer keyword not found after xref table');
|
231
|
|
|
}
|
232
|
|
|
|
233
|
|
|
$data = substr($data, 0, $trailerPos);
|
234
|
|
|
|
235
|
|
|
// get Line-Ending
|
236
|
|
|
preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for linebreaks
|
237
|
|
|
|
238
|
|
|
$differentLineEndings = count(array_unique($m[0]));
|
239
|
|
|
if ($differentLineEndings > 1) {
|
240
|
|
|
$lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY);
|
241
|
|
|
} else {
|
242
|
|
|
$lines = explode($m[0][1], $data);
|
243
|
|
|
}
|
244
|
|
|
|
245
|
|
|
$data = $differentLineEndings = $m = null;
|
246
|
|
|
unset($data, $differentLineEndings, $m);
|
247
|
|
|
|
248
|
|
|
$linesCount = count($lines);
|
249
|
|
|
|
250
|
|
|
$start = 1;
|
251
|
|
|
|
252
|
|
|
for ($i = 0; $i < $linesCount; $i++) {
|
253
|
|
|
$line = trim($lines[$i]);
|
254
|
|
|
if ($line) {
|
255
|
|
|
$pieces = explode(" ", $line);
|
256
|
|
|
$c = count($pieces);
|
257
|
|
|
switch($c) {
|
258
|
|
|
case 2:
|
259
|
|
|
$start = (int)$pieces[0];
|
260
|
|
|
$end = $start+(int)$pieces[1];
|
261
|
|
|
if ($end > $result['max_object'])
|
262
|
|
|
$result['max_object'] = $end;
|
263
|
|
|
break;
|
264
|
|
|
case 3:
|
265
|
|
|
if (!isset($result['xref'][$start]))
|
266
|
|
|
$result['xref'][$start] = array();
|
267
|
|
|
|
268
|
|
|
if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) {
|
269
|
|
|
$result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null;
|
270
|
|
|
}
|
271
|
|
|
$start++;
|
272
|
|
|
break;
|
273
|
|
|
default:
|
274
|
|
|
$this->error('Unexpected data in xref table');
|
275
|
|
|
}
|
276
|
|
|
}
|
277
|
|
|
}
|
278
|
|
|
|
279
|
|
|
$lines = $pieces = $line = $start = $end = $gen = null;
|
280
|
|
|
unset($lines, $pieces, $line, $start, $end, $gen);
|
281
|
|
|
|
282
|
|
|
fseek($this->f, $o_pos+$trailerPos+7);
|
283
|
|
|
|
284
|
|
|
$c =& new pdf_context($this->f);
|
285
|
|
|
$trailer = $this->pdf_read_value($c);
|
286
|
|
|
|
287
|
|
|
$c = null;
|
288
|
|
|
unset($c);
|
289
|
|
|
|
290
|
|
|
if (!isset($result['trailer'])) {
|
291
|
|
|
$result['trailer'] = $trailer;
|
292
|
|
|
}
|
293
|
|
|
|
294
|
|
|
if (isset($trailer[1]['/Prev'])) {
|
295
|
|
|
$this->pdf_read_xref($result, $trailer[1]['/Prev'][1]);
|
296
|
|
|
}
|
297
|
|
|
|
298
|
|
|
$trailer = null;
|
299
|
|
|
unset($trailer);
|
300
|
|
|
|
301
|
|
|
return true;
|
302
|
|
|
}
|
303
|
|
|
|
304
|
|
|
/**
|
305
|
|
|
* Reads an Value
|
306
|
|
|
*
|
307
|
|
|
* @param object $c pdf_context
|
308
|
|
|
* @param string $token a Token
|
309
|
|
|
* @return mixed
|
310
|
|
|
*/
|
311
|
|
|
function pdf_read_value(&$c, $token = null) {
|
|
|
|
|
312
|
|
|
if (is_null($token)) {
|
313
|
|
|
$token = $this->pdf_read_token($c);
|
314
|
|
|
}
|
315
|
|
|
|
316
|
|
|
if ($token === false) {
|
317
|
|
|
return false;
|
318
|
|
|
}
|
319
|
|
|
|
320
|
|
|
switch ($token) {
|
321
|
|
|
case '<':
|
|
|
|
|
322
|
|
|
// This is a hex string.
|
323
|
|
|
// Read the value, then the terminator
|
324
|
|
|
|
325
|
|
|
$pos = $c->offset;
|
326
|
|
|
|
327
|
|
|
while(1) {
|
328
|
|
|
|
329
|
|
|
$match = strpos ($c->buffer, '>', $pos);
|
330
|
|
|
|
331
|
|
|
// If you can't find it, try
|
332
|
|
|
// reading more data from the stream
|
333
|
|
|
|
334
|
|
|
if ($match === false) {
|
335
|
|
|
if (!$c->increase_length()) {
|
336
|
|
|
return false;
|
337
|
|
|
} else {
|
338
|
|
|
continue;
|
339
|
|
|
}
|
340
|
|
|
}
|
341
|
|
|
|
342
|
|
|
$result = substr ($c->buffer, $c->offset, $match - $c->offset);
|
343
|
|
|
$c->offset = $match + 1;
|
344
|
|
|
|
345
|
|
|
return array (PDF_TYPE_HEX, $result);
|
346
|
|
|
}
|
347
|
|
|
|
348
|
|
|
break;
|
349
|
|
View Code Duplication |
case '<<':
|
|
|
|
|
350
|
|
|
// This is a dictionary.
|
351
|
|
|
|
352
|
|
|
$result = array();
|
353
|
|
|
|
354
|
|
|
// Recurse into this function until we reach
|
355
|
|
|
// the end of the dictionary.
|
356
|
|
|
while (($key = $this->pdf_read_token($c)) !== '>>') {
|
357
|
|
|
if ($key === false) {
|
358
|
|
|
return false;
|
359
|
|
|
}
|
360
|
|
|
|
361
|
|
|
if (($value = $this->pdf_read_value($c)) === false) {
|
362
|
|
|
return false;
|
363
|
|
|
}
|
364
|
|
|
$result[$key] = $value;
|
365
|
|
|
}
|
366
|
|
|
|
367
|
|
|
return array (PDF_TYPE_DICTIONARY, $result);
|
368
|
|
|
|
369
|
|
View Code Duplication |
case '[':
|
|
|
|
|
370
|
|
|
// This is an array.
|
371
|
|
|
|
372
|
|
|
$result = array();
|
373
|
|
|
|
374
|
|
|
// Recurse into this function until we reach
|
375
|
|
|
// the end of the array.
|
376
|
|
|
while (($token = $this->pdf_read_token($c)) !== ']') {
|
377
|
|
|
if ($token === false) {
|
378
|
|
|
return false;
|
379
|
|
|
}
|
380
|
|
|
|
381
|
|
|
if (($value = $this->pdf_read_value($c, $token)) === false) {
|
382
|
|
|
return false;
|
383
|
|
|
}
|
384
|
|
|
|
385
|
|
|
$result[] = $value;
|
386
|
|
|
}
|
387
|
|
|
|
388
|
|
|
return array (PDF_TYPE_ARRAY, $result);
|
389
|
|
|
|
390
|
|
|
case '(' :
|
|
|
|
|
391
|
|
|
// This is a string
|
392
|
|
|
$pos = $c->offset;
|
393
|
|
|
|
394
|
|
|
$openBrackets = 1;
|
395
|
|
|
do {
|
396
|
|
|
for (; $openBrackets != 0 && $pos < $c->length; $pos++) {
|
397
|
|
|
switch (ord($c->buffer[$pos])) {
|
398
|
|
|
case 0x28: // '('
|
399
|
|
|
$openBrackets++;
|
400
|
|
|
break;
|
401
|
|
|
case 0x29: // ')'
|
402
|
|
|
$openBrackets--;
|
403
|
|
|
break;
|
404
|
|
|
case 0x5C: // backslash
|
405
|
|
|
$pos++;
|
406
|
|
|
}
|
407
|
|
|
}
|
408
|
|
|
} while($openBrackets != 0 && $c->increase_length());
|
409
|
|
|
|
410
|
|
|
$result = substr($c->buffer, $c->offset, $pos - $c->offset - 1);
|
411
|
|
|
$c->offset = $pos;
|
412
|
|
|
|
413
|
|
|
return array (PDF_TYPE_STRING, $result);
|
414
|
|
|
|
415
|
|
|
|
416
|
|
|
case "stream":
|
417
|
|
|
$o_pos = ftell($c->file)-strlen($c->buffer);
|
418
|
|
|
$o_offset = $c->offset;
|
419
|
|
|
|
420
|
|
|
$c->reset($startpos = $o_pos + $o_offset);
|
421
|
|
|
|
422
|
|
|
$e = 0; // ensure line breaks in front of the stream
|
423
|
|
View Code Duplication |
if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13))
|
|
|
|
|
424
|
|
|
$e++;
|
425
|
|
View Code Duplication |
if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10))
|
|
|
|
|
426
|
|
|
$e++;
|
427
|
|
|
|
428
|
|
|
if ($this->actual_obj[1][1]['/Length'][0] == PDF_TYPE_OBJREF) {
|
429
|
|
|
$tmp_c =& new pdf_context($this->f);
|
430
|
|
|
$tmp_length = $this->pdf_resolve_object($tmp_c,$this->actual_obj[1][1]['/Length']);
|
|
|
|
|
431
|
|
|
$length = $tmp_length[1][1];
|
432
|
|
|
} else {
|
433
|
|
|
$length = $this->actual_obj[1][1]['/Length'][1];
|
434
|
|
|
}
|
435
|
|
|
|
436
|
|
|
if ($length > 0) {
|
437
|
|
|
$c->reset($startpos+$e,$length);
|
438
|
|
|
$v = $c->buffer;
|
439
|
|
|
} else {
|
440
|
|
|
$v = '';
|
441
|
|
|
}
|
442
|
|
|
$c->reset($startpos+$e+$length+9); // 9 = strlen("endstream")
|
|
|
|
|
443
|
|
|
|
444
|
|
|
return array(PDF_TYPE_STREAM, $v);
|
445
|
|
|
|
446
|
|
|
case '%':
|
|
|
|
|
447
|
|
|
// this is a comment - just jump over it
|
448
|
|
|
$pos = $c->offset;
|
449
|
|
|
while(1) {
|
450
|
|
|
// PHP 4.3.3 required
|
451
|
|
|
#$match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos);
|
|
|
|
|
452
|
|
|
// alternative
|
453
|
|
|
$match = preg_match("/(\r\n|\r|\n)/", substr($c->buffer, $pos), $m);
|
454
|
|
|
if ($match === false) {
|
455
|
|
|
if (!$c->increase_length()) {
|
456
|
|
|
return false;
|
457
|
|
|
} else {
|
458
|
|
|
continue;
|
459
|
|
|
}
|
460
|
|
|
}
|
461
|
|
|
|
462
|
|
|
// PHP 4.3.3 required
|
463
|
|
|
#$c->offset = $m[0][1]+strlen($m[0][0]);
|
|
|
|
|
464
|
|
|
// alternative
|
465
|
|
|
$c->offset = strpos($c->buffer, $m[0], $pos)+strlen($m[0]);
|
466
|
|
|
|
467
|
|
|
return $this->pdf_read_value($c);
|
468
|
|
|
}
|
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
|
|
|
if ($token === (string)((int)$token))
|
500
|
|
|
return array (PDF_TYPE_NUMERIC, (int)$token);
|
501
|
|
|
else
|
502
|
|
|
return array (PDF_TYPE_REAL, (float)$token);
|
503
|
|
|
} else if ($token == 'true' || $token == 'false') {
|
504
|
|
|
return array (PDF_TYPE_BOOLEAN, $token == 'true');
|
505
|
|
|
} else {
|
506
|
|
|
|
507
|
|
|
// Just a token. Return it.
|
508
|
|
|
return array (PDF_TYPE_TOKEN, $token);
|
509
|
|
|
}
|
510
|
|
|
|
511
|
|
|
}
|
512
|
|
|
}
|
513
|
|
|
|
514
|
|
|
/**
|
515
|
|
|
* Resolve an object
|
516
|
|
|
*
|
517
|
|
|
* @param object $c pdf_context
|
518
|
|
|
* @param array $obj_spec The object-data
|
519
|
|
|
* @param boolean $encapsulate Must set to true, cause the parsing and fpdi use this method only without this para
|
520
|
|
|
*/
|
521
|
|
|
function pdf_resolve_object(&$c, $obj_spec, $encapsulate = true) {
|
|
|
|
|
522
|
|
|
// Exit if we get invalid data
|
523
|
|
|
if (!is_array($obj_spec)) {
|
524
|
|
|
return false;
|
525
|
|
|
}
|
526
|
|
|
|
527
|
|
|
if ($obj_spec[0] == PDF_TYPE_OBJREF) {
|
528
|
|
|
|
529
|
|
|
// This is a reference, resolve it
|
530
|
|
|
if (isset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]])) {
|
531
|
|
|
|
532
|
|
|
// Save current file position
|
533
|
|
|
// This is needed if you want to resolve
|
534
|
|
|
// references while you're reading another object
|
535
|
|
|
// (e.g.: if you need to determine the length
|
536
|
|
|
// of a stream)
|
537
|
|
|
|
538
|
|
|
$old_pos = ftell($c->file);
|
539
|
|
|
|
540
|
|
|
// Reposition the file pointer and
|
541
|
|
|
// load the object header.
|
542
|
|
|
|
543
|
|
|
$c->reset($this->xref['xref'][$obj_spec[1]][$obj_spec[2]]);
|
544
|
|
|
|
545
|
|
|
$header = $this->pdf_read_value($c,null,true);
|
|
|
|
|
546
|
|
|
|
547
|
|
|
if ($header[0] != PDF_TYPE_OBJDEC || $header[1] != $obj_spec[1] || $header[2] != $obj_spec[2]) {
|
548
|
|
|
$this->error("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
|
549
|
|
|
}
|
550
|
|
|
|
551
|
|
|
// If we're being asked to store all the information
|
552
|
|
|
// about the object, we add the object ID and generation
|
553
|
|
|
// number for later use
|
554
|
|
|
$this->actual_obj =& $result;
|
|
|
|
|
555
|
|
|
if ($encapsulate) {
|
556
|
|
|
$result = array (
|
557
|
|
|
PDF_TYPE_OBJECT,
|
558
|
|
|
'obj' => $obj_spec[1],
|
559
|
|
|
'gen' => $obj_spec[2]
|
560
|
|
|
);
|
561
|
|
|
} else {
|
562
|
|
|
$result = array();
|
563
|
|
|
}
|
564
|
|
|
|
565
|
|
|
// Now simply read the object data until
|
566
|
|
|
// we encounter an end-of-object marker
|
567
|
|
|
while(1) {
|
568
|
|
|
$value = $this->pdf_read_value($c);
|
569
|
|
|
if ($value === false || count($result) > 4) {
|
570
|
|
|
// in this case the parser coudn't find an endobj so we break here
|
571
|
|
|
break;
|
572
|
|
|
}
|
573
|
|
|
|
574
|
|
|
if ($value[0] == PDF_TYPE_TOKEN && $value[1] === 'endobj') {
|
575
|
|
|
break;
|
576
|
|
|
}
|
577
|
|
|
|
578
|
|
|
$result[] = $value;
|
579
|
|
|
}
|
580
|
|
|
|
581
|
|
|
$c->reset($old_pos);
|
582
|
|
|
|
583
|
|
|
if (isset($result[2][0]) && $result[2][0] == PDF_TYPE_STREAM) {
|
584
|
|
|
$result[0] = PDF_TYPE_STREAM;
|
585
|
|
|
}
|
586
|
|
|
|
587
|
|
|
return $result;
|
588
|
|
|
}
|
589
|
|
|
} else {
|
590
|
|
|
return $obj_spec;
|
591
|
|
|
}
|
592
|
|
|
}
|
593
|
|
|
|
594
|
|
|
|
595
|
|
|
|
596
|
|
|
/**
|
597
|
|
|
* Reads a token from the file
|
598
|
|
|
*
|
599
|
|
|
* @param object $c pdf_context
|
600
|
|
|
* @return mixed
|
601
|
|
|
*/
|
602
|
|
|
function pdf_read_token(&$c)
|
|
|
|
|
603
|
|
|
{
|
604
|
|
|
// If there is a token available
|
605
|
|
|
// on the stack, pop it out and
|
606
|
|
|
// return it.
|
607
|
|
|
|
608
|
|
|
if (count($c->stack)) {
|
609
|
|
|
return array_pop($c->stack);
|
610
|
|
|
}
|
611
|
|
|
|
612
|
|
|
// Strip away any whitespace
|
613
|
|
|
|
614
|
|
|
do {
|
615
|
|
|
if (!$c->ensure_content()) {
|
616
|
|
|
return false;
|
617
|
|
|
}
|
618
|
|
|
$c->offset += _strspn($c->buffer, " \n\r\t", $c->offset);
|
619
|
|
|
} while ($c->offset >= $c->length - 1);
|
620
|
|
|
|
621
|
|
|
// Get the first character in the stream
|
622
|
|
|
|
623
|
|
|
$char = $c->buffer[$c->offset++];
|
624
|
|
|
|
625
|
|
|
switch ($char) {
|
626
|
|
|
|
627
|
|
|
case '[' :
|
|
|
|
|
628
|
|
|
case ']' :
|
|
|
|
|
629
|
|
|
case '(' :
|
|
|
|
|
630
|
|
|
case ')' :
|
|
|
|
|
631
|
|
|
|
632
|
|
|
// This is either an array or literal string
|
633
|
|
|
// delimiter, Return it
|
634
|
|
|
|
635
|
|
|
return $char;
|
636
|
|
|
|
637
|
|
|
case '<' :
|
|
|
|
|
638
|
|
|
case '>' :
|
|
|
|
|
639
|
|
|
|
640
|
|
|
// This could either be a hex string or
|
641
|
|
|
// dictionary delimiter. Determine the
|
642
|
|
|
// appropriate case and return the token
|
643
|
|
|
|
644
|
|
|
if ($c->buffer[$c->offset] == $char) {
|
645
|
|
|
if (!$c->ensure_content()) {
|
646
|
|
|
return false;
|
647
|
|
|
}
|
648
|
|
|
$c->offset++;
|
649
|
|
|
return $char . $char;
|
650
|
|
|
} else {
|
651
|
|
|
return $char;
|
652
|
|
|
}
|
653
|
|
|
|
654
|
|
|
default :
|
|
|
|
|
655
|
|
|
|
656
|
|
|
// This is "another" type of token (probably
|
657
|
|
|
// a dictionary entry or a numeric value)
|
658
|
|
|
// Find the end and return it.
|
659
|
|
|
|
660
|
|
|
if (!$c->ensure_content()) {
|
661
|
|
|
return false;
|
662
|
|
|
}
|
663
|
|
|
|
664
|
|
|
while(1) {
|
665
|
|
|
|
666
|
|
|
// Determine the length of the token
|
667
|
|
|
|
668
|
|
|
$pos = _strcspn($c->buffer, " []<>()\r\n\t/", $c->offset);
|
669
|
|
|
if ($c->offset + $pos <= $c->length - 1) {
|
670
|
|
|
break;
|
671
|
|
|
} else {
|
672
|
|
|
// If the script reaches this point,
|
673
|
|
|
// the token may span beyond the end
|
674
|
|
|
// of the current buffer. Therefore,
|
675
|
|
|
// we increase the size of the buffer
|
676
|
|
|
// and try again--just to be safe.
|
677
|
|
|
|
678
|
|
|
$c->increase_length();
|
679
|
|
|
}
|
680
|
|
|
}
|
681
|
|
|
|
682
|
|
|
$result = substr($c->buffer, $c->offset - 1, $pos + 1);
|
|
|
|
|
683
|
|
|
|
684
|
|
|
$c->offset += $pos;
|
685
|
|
|
return $result;
|
686
|
|
|
}
|
687
|
|
|
}
|
688
|
|
|
|
689
|
|
|
|
690
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.