]>
Commit | Line | Data |
---|---|---|
824f8c45 | 1 | <?php |
2 | //============================================================+ | |
3 | // File name : tcpdf_filters.php | |
4 | // Version : 1.0.001 | |
5 | // Begin : 2011-05-23 | |
6 | // Last Update : 2014-04-25 | |
7 | // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com | |
8 | // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) | |
9 | // ------------------------------------------------------------------- | |
10 | // Copyright (C) 2011-2013 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 decoding common PDF filters (PDF 32000-2008 - 7.4 Filters). | |
32 | // | |
33 | //============================================================+ | |
34 | ||
35 | /** | |
36 | * @file | |
37 | * This is a PHP class for decoding common PDF filters (PDF 32000-2008 - 7.4 Filters).<br> | |
38 | * @package com.tecnick.tcpdf | |
39 | * @author Nicola Asuni | |
40 | * @version 1.0.001 | |
41 | */ | |
42 | ||
43 | /** | |
44 | * @class TCPDF_FILTERS | |
45 | * This is a PHP class for decoding common PDF filters (PDF 32000-2008 - 7.4 Filters).<br> | |
46 | * @package com.tecnick.tcpdf | |
47 | * @brief This is a PHP class for decoding common PDF filters. | |
48 | * @version 1.0.001 | |
49 | * @author Nicola Asuni - info@tecnick.com | |
50 | */ | |
51 | class TCPDF_FILTERS { | |
52 | ||
53 | /** | |
54 | * Define a list of available filter decoders. | |
55 | * @private static | |
56 | */ | |
57 | private static $available_filters = array('ASCIIHexDecode', 'ASCII85Decode', 'LZWDecode', 'FlateDecode', 'RunLengthDecode'); | |
58 | ||
59 | // ----------------------------------------------------------------------------- | |
60 | ||
61 | /** | |
62 | * Get a list of available decoding filters. | |
63 | * @return (array) Array of available filter decoders. | |
64 | * @since 1.0.000 (2011-05-23) | |
65 | * @public static | |
66 | */ | |
67 | public static function getAvailableFilters() { | |
68 | return self::$available_filters; | |
69 | } | |
70 | ||
71 | /** | |
72 | * Decode data using the specified filter type. | |
73 | * @param $filter (string) Filter name. | |
74 | * @param $data (string) Data to decode. | |
75 | * @return Decoded data string. | |
76 | * @since 1.0.000 (2011-05-23) | |
77 | * @public static | |
78 | */ | |
79 | public static function decodeFilter($filter, $data) { | |
80 | switch ($filter) { | |
81 | case 'ASCIIHexDecode': { | |
82 | return self::decodeFilterASCIIHexDecode($data); | |
83 | break; | |
84 | } | |
85 | case 'ASCII85Decode': { | |
86 | return self::decodeFilterASCII85Decode($data); | |
87 | break; | |
88 | } | |
89 | case 'LZWDecode': { | |
90 | return self::decodeFilterLZWDecode($data); | |
91 | break; | |
92 | } | |
93 | case 'FlateDecode': { | |
94 | return self::decodeFilterFlateDecode($data); | |
95 | break; | |
96 | } | |
97 | case 'RunLengthDecode': { | |
98 | return self::decodeFilterRunLengthDecode($data); | |
99 | break; | |
100 | } | |
101 | case 'CCITTFaxDecode': { | |
102 | return self::decodeFilterCCITTFaxDecode($data); | |
103 | break; | |
104 | } | |
105 | case 'JBIG2Decode': { | |
106 | return self::decodeFilterJBIG2Decode($data); | |
107 | break; | |
108 | } | |
109 | case 'DCTDecode': { | |
110 | return self::decodeFilterDCTDecode($data); | |
111 | break; | |
112 | } | |
113 | case 'JPXDecode': { | |
114 | return self::decodeFilterJPXDecode($data); | |
115 | break; | |
116 | } | |
117 | case 'Crypt': { | |
118 | return self::decodeFilterCrypt($data); | |
119 | break; | |
120 | } | |
121 | default: { | |
122 | return self::decodeFilterStandard($data); | |
123 | break; | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | // --- FILTERS (PDF 32000-2008 - 7.4 Filters) ------------------------------ | |
129 | ||
130 | /** | |
131 | * Standard | |
132 | * Default decoding filter (leaves data unchanged). | |
133 | * @param $data (string) Data to decode. | |
134 | * @return Decoded data string. | |
135 | * @since 1.0.000 (2011-05-23) | |
136 | * @public static | |
137 | */ | |
138 | public static function decodeFilterStandard($data) { | |
139 | return $data; | |
140 | } | |
141 | ||
142 | /** | |
143 | * ASCIIHexDecode | |
144 | * Decodes data encoded in an ASCII hexadecimal representation, reproducing the original binary data. | |
145 | * @param $data (string) Data to decode. | |
146 | * @return Decoded data string. | |
147 | * @since 1.0.000 (2011-05-23) | |
148 | * @public static | |
149 | */ | |
150 | public static function decodeFilterASCIIHexDecode($data) { | |
151 | // intialize string to return | |
152 | $decoded = ''; | |
153 | // all white-space characters shall be ignored | |
154 | $data = preg_replace('/[\s]/', '', $data); | |
155 | // check for EOD character: GREATER-THAN SIGN (3Eh) | |
156 | $eod = strpos($data, '>'); | |
157 | if ($eod !== false) { | |
158 | // remove EOD and extra data (if any) | |
159 | $data = substr($data, 0, $eod); | |
160 | $eod = true; | |
161 | } | |
162 | // get data length | |
163 | $data_length = strlen($data); | |
164 | if (($data_length % 2) != 0) { | |
165 | // odd number of hexadecimal digits | |
166 | if ($eod) { | |
167 | // EOD shall behave as if a 0 (zero) followed the last digit | |
168 | $data = substr($data, 0, -1).'0'.substr($data, -1); | |
169 | } else { | |
170 | self::Error('decodeFilterASCIIHexDecode: invalid code'); | |
171 | } | |
172 | } | |
173 | // check for invalid characters | |
174 | if (preg_match('/[^a-fA-F\d]/', $data) > 0) { | |
175 | self::Error('decodeFilterASCIIHexDecode: invalid code'); | |
176 | } | |
177 | // get one byte of binary data for each pair of ASCII hexadecimal digits | |
178 | $decoded = pack('H*', $data); | |
179 | return $decoded; | |
180 | } | |
181 | ||
182 | /** | |
183 | * ASCII85Decode | |
184 | * Decodes data encoded in an ASCII base-85 representation, reproducing the original binary data. | |
185 | * @param $data (string) Data to decode. | |
186 | * @return Decoded data string. | |
187 | * @since 1.0.000 (2011-05-23) | |
188 | * @public static | |
189 | */ | |
190 | public static function decodeFilterASCII85Decode($data) { | |
191 | // intialize string to return | |
192 | $decoded = ''; | |
193 | // all white-space characters shall be ignored | |
194 | $data = preg_replace('/[\s]/', '', $data); | |
195 | // remove start sequence 2-character sequence <~ (3Ch)(7Eh) | |
196 | if (strpos($data, '<~') !== false) { | |
197 | // remove EOD and extra data (if any) | |
198 | $data = substr($data, 2); | |
199 | } | |
200 | // check for EOD: 2-character sequence ~> (7Eh)(3Eh) | |
201 | $eod = strpos($data, '~>'); | |
202 | if ($eod !== false) { | |
203 | // remove EOD and extra data (if any) | |
204 | $data = substr($data, 0, $eod); | |
205 | } | |
206 | // data length | |
207 | $data_length = strlen($data); | |
208 | // check for invalid characters | |
209 | if (preg_match('/[^\x21-\x75,\x74]/', $data) > 0) { | |
210 | self::Error('decodeFilterASCII85Decode: invalid code'); | |
211 | } | |
212 | // z sequence | |
213 | $zseq = chr(0).chr(0).chr(0).chr(0); | |
214 | // position inside a group of 4 bytes (0-3) | |
215 | $group_pos = 0; | |
216 | $tuple = 0; | |
217 | $pow85 = array((85*85*85*85), (85*85*85), (85*85), 85, 1); | |
218 | $last_pos = ($data_length - 1); | |
219 | // for each byte | |
220 | for ($i = 0; $i < $data_length; ++$i) { | |
221 | // get char value | |
222 | $char = ord($data[$i]); | |
223 | if ($char == 122) { // 'z' | |
224 | if ($group_pos == 0) { | |
225 | $decoded .= $zseq; | |
226 | } else { | |
227 | self::Error('decodeFilterASCII85Decode: invalid code'); | |
228 | } | |
229 | } else { | |
230 | // the value represented by a group of 5 characters should never be greater than 2^32 - 1 | |
231 | $tuple += (($char - 33) * $pow85[$group_pos]); | |
232 | if ($group_pos == 4) { | |
233 | $decoded .= chr($tuple >> 24).chr($tuple >> 16).chr($tuple >> 8).chr($tuple); | |
234 | $tuple = 0; | |
235 | $group_pos = 0; | |
236 | } else { | |
237 | ++$group_pos; | |
238 | } | |
239 | } | |
240 | } | |
241 | if ($group_pos > 1) { | |
242 | $tuple += $pow85[($group_pos - 1)]; | |
243 | } | |
244 | // last tuple (if any) | |
245 | switch ($group_pos) { | |
246 | case 4: { | |
247 | $decoded .= chr($tuple >> 24).chr($tuple >> 16).chr($tuple >> 8); | |
248 | break; | |
249 | } | |
250 | case 3: { | |
251 | $decoded .= chr($tuple >> 24).chr($tuple >> 16); | |
252 | break; | |
253 | } | |
254 | case 2: { | |
255 | $decoded .= chr($tuple >> 24); | |
256 | break; | |
257 | } | |
258 | case 1: { | |
259 | self::Error('decodeFilterASCII85Decode: invalid code'); | |
260 | break; | |
261 | } | |
262 | } | |
263 | return $decoded; | |
264 | } | |
265 | ||
266 | /** | |
267 | * LZWDecode | |
268 | * Decompresses data encoded using the LZW (Lempel-Ziv-Welch) adaptive compression method, reproducing the original text or binary data. | |
269 | * @param $data (string) Data to decode. | |
270 | * @return Decoded data string. | |
271 | * @since 1.0.000 (2011-05-23) | |
272 | * @public static | |
273 | */ | |
274 | public static function decodeFilterLZWDecode($data) { | |
275 | // intialize string to return | |
276 | $decoded = ''; | |
277 | // data length | |
278 | $data_length = strlen($data); | |
279 | // convert string to binary string | |
280 | $bitstring = ''; | |
281 | for ($i = 0; $i < $data_length; ++$i) { | |
282 | $bitstring .= sprintf('%08b', ord($data{$i})); | |
283 | } | |
284 | // get the number of bits | |
285 | $data_length = strlen($bitstring); | |
286 | // initialize code length in bits | |
287 | $bitlen = 9; | |
288 | // initialize dictionary index | |
289 | $dix = 258; | |
290 | // initialize the dictionary (with the first 256 entries). | |
291 | $dictionary = array(); | |
292 | for ($i = 0; $i < 256; ++$i) { | |
293 | $dictionary[$i] = chr($i); | |
294 | } | |
295 | // previous val | |
296 | $prev_index = 0; | |
297 | // while we encounter EOD marker (257), read code_length bits | |
298 | while (($data_length > 0) AND (($index = bindec(substr($bitstring, 0, $bitlen))) != 257)) { | |
299 | // remove read bits from string | |
300 | $bitstring = substr($bitstring, $bitlen); | |
301 | // update number of bits | |
302 | $data_length -= $bitlen; | |
303 | if ($index == 256) { // clear-table marker | |
304 | // reset code length in bits | |
305 | $bitlen = 9; | |
306 | // reset dictionary index | |
307 | $dix = 258; | |
308 | $prev_index = 256; | |
309 | // reset the dictionary (with the first 256 entries). | |
310 | $dictionary = array(); | |
311 | for ($i = 0; $i < 256; ++$i) { | |
312 | $dictionary[$i] = chr($i); | |
313 | } | |
314 | } elseif ($prev_index == 256) { | |
315 | // first entry | |
316 | $decoded .= $dictionary[$index]; | |
317 | $prev_index = $index; | |
318 | } else { | |
319 | // check if index exist in the dictionary | |
320 | if ($index < $dix) { | |
321 | // index exist on dictionary | |
322 | $decoded .= $dictionary[$index]; | |
323 | $dic_val = $dictionary[$prev_index].$dictionary[$index][0]; | |
324 | // store current index | |
325 | $prev_index = $index; | |
326 | } else { | |
327 | // index do not exist on dictionary | |
328 | $dic_val = $dictionary[$prev_index].$dictionary[$prev_index][0]; | |
329 | $decoded .= $dic_val; | |
330 | } | |
331 | // update dictionary | |
332 | $dictionary[$dix] = $dic_val; | |
333 | ++$dix; | |
334 | // change bit length by case | |
335 | if ($dix == 2047) { | |
336 | $bitlen = 12; | |
337 | } elseif ($dix == 1023) { | |
338 | $bitlen = 11; | |
339 | } elseif ($dix == 511) { | |
340 | $bitlen = 10; | |
341 | } | |
342 | } | |
343 | } | |
344 | return $decoded; | |
345 | } | |
346 | ||
347 | /** | |
348 | * FlateDecode | |
349 | * Decompresses data encoded using the zlib/deflate compression method, reproducing the original text or binary data. | |
350 | * @param $data (string) Data to decode. | |
351 | * @return Decoded data string. | |
352 | * @since 1.0.000 (2011-05-23) | |
353 | * @public static | |
354 | */ | |
355 | public static function decodeFilterFlateDecode($data) { | |
356 | // intialize string to return | |
357 | $decoded = @gzuncompress($data); | |
358 | if ($decoded === false) { | |
359 | self::Error('decodeFilterFlateDecode: invalid code'); | |
360 | } | |
361 | return $decoded; | |
362 | } | |
363 | ||
364 | /** | |
365 | * RunLengthDecode | |
366 | * Decompresses data encoded using a byte-oriented run-length encoding algorithm. | |
367 | * @param $data (string) Data to decode. | |
368 | * @since 1.0.000 (2011-05-23) | |
369 | * @public static | |
370 | */ | |
371 | public static function decodeFilterRunLengthDecode($data) { | |
372 | // intialize string to return | |
373 | $decoded = ''; | |
374 | // data length | |
375 | $data_length = strlen($data); | |
376 | $i = 0; | |
377 | while($i < $data_length) { | |
378 | // get current byte value | |
379 | $byte = ord($data{$i}); | |
380 | if ($byte == 128) { | |
381 | // a length value of 128 denote EOD | |
382 | break; | |
383 | } elseif ($byte < 128) { | |
384 | // if the length byte is in the range 0 to 127 | |
385 | // the following length + 1 (1 to 128) bytes shall be copied literally during decompression | |
386 | $decoded .= substr($data, ($i + 1), ($byte + 1)); | |
387 | // move to next block | |
388 | $i += ($byte + 2); | |
389 | } else { | |
390 | // if length is in the range 129 to 255, | |
391 | // the following single byte shall be copied 257 - length (2 to 128) times during decompression | |
392 | $decoded .= str_repeat($data{($i + 1)}, (257 - $byte)); | |
393 | // move to next block | |
394 | $i += 2; | |
395 | } | |
396 | } | |
397 | return $decoded; | |
398 | } | |
399 | ||
400 | /** | |
401 | * CCITTFaxDecode (NOT IMPLEMETED - RETURN AN EXCEPTION) | |
402 | * Decompresses data encoded using the CCITT facsimile standard, reproducing the original data (typically monochrome image data at 1 bit per pixel). | |
403 | * @param $data (string) Data to decode. | |
404 | * @return Decoded data string. | |
405 | * @since 1.0.000 (2011-05-23) | |
406 | * @public static | |
407 | */ | |
408 | public static function decodeFilterCCITTFaxDecode($data) { | |
409 | self::Error('~decodeFilterCCITTFaxDecode: this method has not been yet implemented'); | |
410 | //return $data; | |
411 | } | |
412 | ||
413 | /** | |
414 | * JBIG2Decode (NOT IMPLEMETED - RETURN AN EXCEPTION) | |
415 | * Decompresses data encoded using the JBIG2 standard, reproducing the original monochrome (1 bit per pixel) image data (or an approximation of that data). | |
416 | * @param $data (string) Data to decode. | |
417 | * @return Decoded data string. | |
418 | * @since 1.0.000 (2011-05-23) | |
419 | * @public static | |
420 | */ | |
421 | public static function decodeFilterJBIG2Decode($data) { | |
422 | self::Error('~decodeFilterJBIG2Decode: this method has not been yet implemented'); | |
423 | //return $data; | |
424 | } | |
425 | ||
426 | /** | |
427 | * DCTDecode (NOT IMPLEMETED - RETURN AN EXCEPTION) | |
428 | * Decompresses data encoded using a DCT (discrete cosine transform) technique based on the JPEG standard, reproducing image sample data that approximates the original data. | |
429 | * @param $data (string) Data to decode. | |
430 | * @return Decoded data string. | |
431 | * @since 1.0.000 (2011-05-23) | |
432 | * @public static | |
433 | */ | |
434 | public static function decodeFilterDCTDecode($data) { | |
435 | self::Error('~decodeFilterDCTDecode: this method has not been yet implemented'); | |
436 | //return $data; | |
437 | } | |
438 | ||
439 | /** | |
440 | * JPXDecode (NOT IMPLEMETED - RETURN AN EXCEPTION) | |
441 | * Decompresses data encoded using the wavelet-based JPEG2000 standard, reproducing the original image data. | |
442 | * @param $data (string) Data to decode. | |
443 | * @return Decoded data string. | |
444 | * @since 1.0.000 (2011-05-23) | |
445 | * @public static | |
446 | */ | |
447 | public static function decodeFilterJPXDecode($data) { | |
448 | self::Error('~decodeFilterJPXDecode: this method has not been yet implemented'); | |
449 | //return $data; | |
450 | } | |
451 | ||
452 | /** | |
453 | * Crypt (NOT IMPLEMETED - RETURN AN EXCEPTION) | |
454 | * Decrypts data encrypted by a security handler, reproducing the data as it was before encryption. | |
455 | * @param $data (string) Data to decode. | |
456 | * @return Decoded data string. | |
457 | * @since 1.0.000 (2011-05-23) | |
458 | * @public static | |
459 | */ | |
460 | public static function decodeFilterCrypt($data) { | |
461 | self::Error('~decodeFilterCrypt: this method has not been yet implemented'); | |
462 | //return $data; | |
463 | } | |
464 | ||
465 | // --- END FILTERS SECTION ------------------------------------------------- | |
466 | ||
467 | /** | |
468 | * Throw an exception. | |
469 | * @param $msg (string) The error message | |
470 | * @since 1.0.000 (2011-05-23) | |
471 | * @public static | |
472 | */ | |
473 | public static function Error($msg) { | |
474 | throw new Exception('TCPDF_PARSER ERROR: '.$msg); | |
475 | } | |
476 | ||
477 | } // END OF TCPDF_FILTERS CLASS | |
478 | ||
479 | //============================================================+ | |
480 | // END OF FILE | |
481 | //============================================================+ |