1<?php
2/**
3 * This file is part of FPDI
4 *
5 * @package   setasign\Fpdi
6 * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com)
7 * @license   http://opensource.org/licenses/mit-license The MIT License
8 */
9
10namespace setasign\Fpdi\PdfParser\Filter;
11
12/**
13 * Class for handling zlib/deflate encoded data
14 *
15 * @package setasign\Fpdi\PdfParser\Filter
16 */
17class Flate implements FilterInterface
18{
19    /**
20     * Checks whether the zlib extension is loaded.
21     *
22     * Used for testing purpose.
23     *
24     * @return boolean
25     * @internal
26     */
27    protected function extensionLoaded()
28    {
29        return \extension_loaded('zlib');
30    }
31
32    /**
33     * Decodes a flate compressed string.
34     *
35     * @param string $data The input string
36     * @return string
37     * @throws FlateException
38     */
39    public function decode($data)
40    {
41        if ($this->extensionLoaded()) {
42            $oData = $data;
43            $data = @((\strlen($data) > 0) ? \gzuncompress($data) : '');
44            if ($data === false) {
45                // let's try if the checksum is CRC32
46                $fh = fopen('php://temp', 'w+b');
47                fwrite($fh, "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $oData);
48                stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 30]);
49                fseek($fh, 0);
50                $data = stream_get_contents($fh);
51                fclose($fh);
52
53                if ($data) {
54                    return $data;
55                }
56
57                // Try this fallback
58                $tries = 0;
59
60                $oDataLen = strlen($oData);
61                while ($tries < 6 && ($data === false || (strlen($data) < (strlen($oDataLen) - $tries - 1)))) {
62                    $data = @(gzinflate(substr($oData, $tries)));
63                    $tries++;
64                }
65
66                // let's use this fallback only if the $data is longer than the original data
67                if (strlen($data) > ($oDataLen - $tries - 1)) {
68                    return $data;
69                }
70
71                if (!$data) {
72                    throw new FlateException(
73                        'Error while decompressing stream.',
74                        FlateException::DECOMPRESS_ERROR
75                    );
76                }
77            }
78        } else {
79            throw new FlateException(
80                'To handle FlateDecode filter, enable zlib support in PHP.',
81                FlateException::NO_ZLIB
82            );
83        }
84
85        return $data;
86    }
87}
88