Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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  	 	 // initialize 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  	 	 // initialize 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  	 	 // initialize 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  	 	 // initialize 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  	 	 // initialize 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  //============================================================+