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 ASCII base-85 encoded data
14 *
15 * @package setasign\Fpdi\PdfParser\Filter
16 */
17class Ascii85 implements FilterInterface
18{
19    /**
20     * Decode ASCII85 encoded string.
21     *
22     * @param string $data The input string
23     * @return string
24     * @throws Ascii85Exception
25     */
26    public function decode($data)
27    {
28        $out = '';
29        $state = 0;
30        $chn = null;
31
32        $data = \preg_replace('/\s/', '', $data);
33
34        $l = \strlen($data);
35
36        /** @noinspection ForeachInvariantsInspection */
37        for ($k = 0; $k < $l; ++$k) {
38            $ch = \ord($data[$k]) & 0xff;
39
40            //Start <~
41            if ($k === 0 && $ch === 60 && isset($data[$k + 1]) && (\ord($data[$k + 1]) & 0xFF) === 126) {
42                $k++;
43                continue;
44            }
45            //End ~>
46            if ($ch === 126 && isset($data[$k + 1]) && (\ord($data[$k + 1]) & 0xFF) === 62) {
47                break;
48            }
49
50            if ($ch === 122 /* z */ && $state === 0) {
51                $out .= \chr(0) . \chr(0) . \chr(0) . \chr(0);
52                continue;
53            }
54
55            if ($ch < 33 /* ! */ || $ch > 117 /* u */) {
56                throw new Ascii85Exception(
57                    'Illegal character found while ASCII85 decode.',
58                    Ascii85Exception::ILLEGAL_CHAR_FOUND
59                );
60            }
61
62            $chn[$state] = $ch - 33;/* ! */
63            $state++;
64
65            if ($state === 5) {
66                $state = 0;
67                $r = 0;
68                for ($j = 0; $j < 5; ++$j) {
69                    /** @noinspection UnnecessaryCastingInspection */
70                    $r = (int)($r * 85 + $chn[$j]);
71                }
72
73                $out .= \chr($r >> 24)
74                    . \chr($r >> 16)
75                    . \chr($r >> 8)
76                    . \chr($r);
77            }
78        }
79
80        if ($state === 1) {
81            throw new Ascii85Exception(
82                'Illegal length while ASCII85 decode.',
83                Ascii85Exception::ILLEGAL_LENGTH
84            );
85        }
86
87        if ($state === 2) {
88            $r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1] + 1) * 85 * 85 * 85;
89            $out .= \chr($r >> 24);
90
91        } elseif ($state === 3) {
92            $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2] + 1) * 85 * 85;
93            $out .= \chr($r >> 24);
94            $out .= \chr($r >> 16);
95
96        } elseif ($state === 4) {
97            $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3] + 1) * 85;
98            $out .= \chr($r >> 24);
99            $out .= \chr($r >> 16);
100            $out .= \chr($r >> 8);
101        }
102
103        return $out;
104    }
105}
106