1: <?php
2: /**
3: * XrefStream.php
4: *
5: * @since 2011-05-23
6: * @category Library
7: * @package PdfParser
8: * @author Nicola Asuni <info@tecnick.com>
9: * @copyright 2011-2015 Nicola Asuni - Tecnick.com LTD
10: * @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
11: * @link https://github.com/tecnickcom/tc-lib-pdf-parser
12: *
13: * This file is part of tc-lib-pdf-parser software library.
14: */
15:
16: namespace Com\Tecnick\Pdf\Parser\Process;
17:
18: use \Com\Tecnick\Pdf\Parser\Exception as PPException;
19:
20: /**
21: * Com\Tecnick\Pdf\Parser\Process\XrefStream
22: *
23: * Process XREF
24: *
25: * @since 2011-05-23
26: * @category Library
27: * @package PdfParser
28: * @author Nicola Asuni <info@tecnick.com>
29: * @copyright 2011-2015 Nicola Asuni - Tecnick.com LTD
30: * @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
31: * @link https://github.com/tecnickcom/tc-lib-pdf-parser
32: */
33: abstract class XrefStream extends \Com\Tecnick\Pdf\Parser\Process\RawObject
34: {
35: /**
36: * Process object indexes
37: *
38: * @param array $xref
39: * @param int $obj_num
40: * @param array $sdata
41: */
42: protected function processObjIndexes(&$xref, &$obj_num, $sdata)
43: {
44: foreach ($sdata as $row) {
45: switch ($row[0]) {
46: case 0:
47: // (f) linked list of free objects
48: break;
49: case 1:
50: // (n) objects that are in use but are not compressed
51: // create unique object index: [object number]_[generation number]
52: $index = $obj_num.'_'.$row[2];
53: // check if object already exist
54: if (!isset($xref['xref'][$index])) {
55: // store object offset position
56: $xref['xref'][$index] = $row[1];
57: }
58: break;
59: case 2:
60: // compressed objects
61: // $row[1] = object number of the object stream in which this object is stored
62: // $row[2] = index of this object within the object stream
63: $index = $row[1].'_0_'.$row[2];
64: $xref['xref'][$index] = -1;
65: break;
66: default:
67: // null objects
68: break;
69: }
70: ++$obj_num;
71: }
72: }
73:
74: /**
75: * PNG Unpredictor
76: *
77: * @param array $sdata
78: * @param array $ddata
79: * @param int $columns
80: * @param int $prev_row
81: */
82: protected function pngUnpredictor($sdata, &$ddata, $columns, $prev_row)
83: {
84: // for each row apply PNG unpredictor
85: foreach ($sdata as $key => $row) {
86: // initialize new row
87: $ddata[$key] = array();
88: // get PNG predictor value
89: $predictor = (10 + $row[0]);
90: // for each byte on the row
91: for ($idx = 1; $idx <= $columns; ++$idx) {
92: // new index
93: $jdx = ($idx - 1);
94: $row_up = $prev_row[$jdx];
95: if ($idx == 1) {
96: $row_left = 0;
97: $row_upleft = 0;
98: } else {
99: $row_left = $row[($idx - 1)];
100: $row_upleft = $prev_row[($jdx - 1)];
101: }
102: switch ($predictor) {
103: case 10:
104: // PNG prediction (on encoding, PNG None on all rows)
105: $ddata[$key][$jdx] = $row[$idx];
106: break;
107: case 11:
108: // PNG prediction (on encoding, PNG Sub on all rows)
109: $ddata[$key][$jdx] = (($row[$idx] + $row_left) & 0xff);
110: break;
111: case 12:
112: // PNG prediction (on encoding, PNG Up on all rows)
113: $ddata[$key][$jdx] = (($row[$idx] + $row_up) & 0xff);
114: break;
115: case 13:
116: // PNG prediction (on encoding, PNG Average on all rows)
117: $ddata[$key][$jdx] = (($row[$idx] + (($row_left + $row_up) / 2)) & 0xff);
118: break;
119: case 14:
120: // PNG prediction (on encoding, PNG Paeth on all rows)
121: $this->minDistance($ddata, $row, $idx, $jdx, $row_left, $row_up, $row_upleft);
122: break;
123: default:
124: // PNG prediction (on encoding, PNG optimum)
125: throw new PPException('Unknown PNG predictor');
126: break;
127: }
128: }
129: $prev_row = $ddata[$key];
130: } // end for each row
131: }
132:
133: /**
134: * Return minimum distance for PNG unpredictor
135: *
136: * @param array $ddata
137: * @param array $row
138: * @param int $idx
139: * @param int $jdx
140: * @param int $row_left
141: * @param int $row_up
142: * @param int $row_upleft
143: */
144: protected function minDistance(&$ddata, $row, $idx, $jdx, $row_left, $row_up, $row_upleft)
145: {
146: // initial estimate
147: $pos = ($row_left + $row_up - $row_upleft);
148: // distances
149: $psa = abs($pos - $row_left);
150: $psb = abs($pos - $row_up);
151: $psc = abs($pos - $row_upleft);
152: $pmin = min($psa, $psb, $psc);
153: switch ($pmin) {
154: case $psa:
155: $ddata[$key][$jdx] = (($row[$idx] + $row_left) & 0xff);
156: break;
157: case $psb:
158: $ddata[$key][$jdx] = (($row[$idx] + $row_up) & 0xff);
159: break;
160: case $psc:
161: $ddata[$key][$jdx] = (($row[$idx] + $row_upleft) & 0xff);
162: break;
163: }
164: }
165:
166: /**
167: * Process XREF types
168: *
169: * @param array $sarr
170: * @param array $xref
171: * @param array $wbt
172: * @param int $index_first
173: * @param int $prevxref
174: * @param int $columns
175: * @param bool $valid_crs
176: * @param bool $filltrailer
177: */
178: protected function processXrefType(
179: $sarr,
180: &$xref,
181: &$wbt,
182: &$index_first,
183: &$prevxref,
184: &$columns,
185: &$valid_crs,
186: $filltrailer
187: ) {
188: foreach ($sarr as $key => $val) {
189: if ($val[0] !== '/') {
190: continue;
191: }
192: switch ($val[1]) {
193: case 'Type':
194: $valid_crs = (($sarr[($key + 1)][0] == '/') && ($sarr[($key + 1)][1] == 'XRef'));
195: break;
196: case 'Index':
197: // first object number in the subsection
198: $index_first = intval($sarr[($key + 1)][1][0][1]);
199: // number of entries in the subsection
200: // $index_entries = intval($sarr[($key + 1)][1][1][1]);
201: break;
202: case 'Prev':
203: $this->processXrefPrev($sarr, $key, $prevxref);
204: break;
205: case 'W':
206: // number of bytes (in the decoded stream) of the corresponding field
207: $wbt[0] = intval($sarr[($key + 1)][1][0][1]);
208: $wbt[1] = intval($sarr[($key + 1)][1][1][1]);
209: $wbt[2] = intval($sarr[($key + 1)][1][2][1]);
210: break;
211: case 'DecodeParms':
212: $this->processXrefDecodeParms($sarr, $key, $columns);
213: break;
214: }
215: $this->processXrefTypeFt($val[1], $sarr, $xref, $filltrailer);
216: }
217: }
218:
219: /**
220: * Process XREF type Prev
221: *
222: * @param array $sarr
223: * @param int $key
224: * @param int $prevxref
225:
226: */
227: protected function processXrefPrev($sarr, $key, &$prevxref)
228: {
229: if ($sarr[($key + 1)][0] == 'numeric') {
230: // get previous xref offset
231: $prevxref = intval($sarr[($key + 1)][1]);
232: }
233: }
234:
235: /**
236: * Process XREF type DecodeParms
237: *
238: * @param array $sarr
239: * @param int $key
240: * @param int $columns
241: */
242: protected function processXrefDecodeParms($sarr, $key, &$columns)
243: {
244: $decpar = $sarr[($key + 1)][1];
245: foreach ($decpar as $kdc => $vdc) {
246: if (($vdc[0] == '/') && ($vdc[1] == 'Columns') && ($decpar[($kdc + 1)][0] == 'numeric')) {
247: $columns = intval($decpar[($kdc + 1)][1]);
248: break;
249: }
250: }
251: }
252:
253: /**
254: * Process XREF type
255: *
256: * @param string $type
257: * @param array $sarr
258: * @param array $xref
259: * @param bool $filltrailer
260: */
261: protected function processXrefTypeFt($type, $sarr, &$xref, $filltrailer)
262: {
263: if (!$filltrailer) {
264: return;
265: }
266: switch ($type) {
267: case 'Size':
268: if ($sarr[($key + 1)][0] == 'numeric') {
269: $xref['trailer']['size'] = $sarr[($key + 1)][1];
270: }
271: break;
272: case 'ID':
273: $xref['trailer']['id'] = array();
274: $xref['trailer']['id'][0] = $sarr[($key + 1)][1][0][1];
275: $xref['trailer']['id'][1] = $sarr[($key + 1)][1][1][1];
276: break;
277: default:
278: $this->processXrefObjref($type, $sarr, $xref);
279: break;
280: }
281: }
282:
283: /**
284: * Process XREF type Objref
285: *
286: * @param string $type
287: * @param array $sarr
288: * @param array $xref
289: */
290: protected function processXrefObjref($type, $sarr, &$xref)
291: {
292: if (!isset($sarr[($key + 1)]) || ($sarr[($key + 1)][0] !== 'objref')) {
293: return;
294: }
295: switch ($type) {
296: case 'Root':
297: $xref['trailer']['root'] = $sarr[($key + 1)][1];
298: break;
299: case 'Info':
300: $xref['trailer']['info'] = $sarr[($key + 1)][1];
301: break;
302: case 'Encrypt':
303: $xref['trailer']['encrypt'] = $sarr[($key + 1)][1];
304: break;
305: }
306: }
307: }
308: