summaryrefslogtreecommitdiffstats
path: root/vendor/setasign/fpdi/src/PdfParser/Type/PdfString.php
blob: 1636e68d60f62e6e70d7f53ca59ad13b63d3fdfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<?php

/**
 * This file is part of FPDI
 *
 * @package   setasign\Fpdi
 * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com)
 * @license   http://opensource.org/licenses/mit-license The MIT License
 */

namespace setasign\Fpdi\PdfParser\Type;

use setasign\Fpdi\PdfParser\StreamReader;

/**
 * Class representing a PDF string object
 */
class PdfString extends PdfType
{
    /**
     * Parses a string object from the stream reader.
     *
     * @param StreamReader $streamReader
     * @return self
     */
    public static function parse(StreamReader $streamReader)
    {
        $pos = $startPos = $streamReader->getOffset();
        $openBrackets = 1;
        do {
            $buffer = $streamReader->getBuffer(false);
            for ($length = \strlen($buffer); $openBrackets !== 0 && $pos < $length; $pos++) {
                switch ($buffer[$pos]) {
                    case '(':
                        $openBrackets++;
                        break;
                    case ')':
                        $openBrackets--;
                        break;
                    case '\\':
                        $pos++;
                }
            }
        } while ($openBrackets !== 0 && $streamReader->increaseLength());

        $result = \substr($buffer, $startPos, $openBrackets + $pos - $startPos - 1);
        $streamReader->setOffset($pos);

        $v = new self();
        $v->value = $result;

        return $v;
    }

    /**
     * Helper method to create an instance.
     *
     * @param string $value The string needs to be escaped accordingly.
     * @return self
     */
    public static function create($value)
    {
        $v = new self();
        $v->value = $value;

        return $v;
    }

    /**
     * Ensures that the passed value is a PdfString instance.
     *
     * @param mixed $string
     * @return self
     * @throws PdfTypeException
     */
    public static function ensure($string)
    {
        return PdfType::ensureType(self::class, $string, 'String value expected.');
    }

    /**
     * Unescapes escaped sequences in a PDF string according to the PDF specification.
     *
     * @param string $s
     * @return string
     */
    public static function unescape($s)
    {
        $out = '';
        /** @noinspection ForeachInvariantsInspection */
        for ($count = 0, $n = \strlen($s); $count < $n; $count++) {
            if ($s[$count] !== '\\') {
                $out .= $s[$count];
            } else {
                // A backslash at the end of the string - ignore it
                if ($count === ($n - 1)) {
                    break;
                }

                switch ($s[++$count]) {
                    case ')':
                    case '(':
                    case '\\':
                        $out .= $s[$count];
                        break;

                    case 'f':
                        $out .= "\x0C";
                        break;

                    case 'b':
                        $out .= "\x08";
                        break;

                    case 't':
                        $out .= "\x09";
                        break;

                    case 'r':
                        $out .= "\x0D";
                        break;

                    case 'n':
                        $out .= "\x0A";
                        break;

                    case "\r":
                        if ($count !== $n - 1 && $s[$count + 1] === "\n") {
                            $count++;
                        }
                        break;

                    case "\n":
                        break;

                    default:
                        $actualChar = \ord($s[$count]);
                        // ascii 48 = number 0
                        // ascii 57 = number 9
                        if ($actualChar >= 48 && $actualChar <= 57) {
                            $oct = '' . $s[$count];

                            /** @noinspection NotOptimalIfConditionsInspection */
                            if (
                                $count + 1 < $n
                                && \ord($s[$count + 1]) >= 48
                                && \ord($s[$count + 1]) <= 57
                            ) {
                                $count++;
                                $oct .= $s[$count];

                                /** @noinspection NotOptimalIfConditionsInspection */
                                if (
                                    $count + 1 < $n
                                    && \ord($s[$count + 1]) >= 48
                                    && \ord($s[$count + 1]) <= 57
                                ) {
                                    $oct .= $s[++$count];
                                }
                            }

                            $out .= \chr(\octdec($oct));
                        } else {
                            // If the character is not one of those defined, the backslash is ignored
                            $out .= $s[$count];
                        }
                }
            }
        }
        return $out;
    }
}