Completed
Push — master ( 245614...5f2e6b )
by Ben
02:14
created

ObjectWriter::startStream()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * BaconPdf
4
 *
5
 * @link      http://github.com/Bacon/BaconPdf For the canonical source repository
6
 * @copyright 2015 Ben Scholzen (DASPRiD)
7
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
8
 */
9
10
namespace Bacon\Pdf\Writer;
11
12
use Bacon\Pdf\Exception\InvalidArgumentException;
13
use SplFileObject;
14
15
/**
16
 * Writer responsible for writing objects to a stream.
17
 *
18
 * While the PDF specification tells that there is a line limit of 255 characters, not even Adobe's own PDF library
19
 * respects this limit. We ignore it as well, as it imposes a huge impact on the performance of the writer.
20
 *
21
 * {@internal This is a very performance sensitive class, which is why some code may look duplicated. Before thinking
22
 * about refactoring these parts, take a good look and the supplied benchmarks and verify that your changes
23
 * do not affect the performance in a bad way. Keep in mind that the methods in this writer are called quite
24
 * often.}}
25
 */
26
class ObjectWriter
27
{
28
    /**
29
     * @var SplFileObject
30
     */
31
    private $fileObject;
32
33
    /**
34
     * @var bool
35
     */
36
    private $requiresWhitespace = false;
37
38
    /**
39
     * @var int
40
     */
41
    private $lastAllocatedObjectId = 0;
42
43
    /**
44
     * @var int[]
45
     */
46
    private $objectOffsets = [];
47
48
    /**
49
     * @param SplFileObject $fileObject
50
     */
51
    public function __construct(SplFileObject $fileObject)
52
    {
53
        $this->fileObject = $fileObject;
54
    }
55
56
    /**
57
     * Returns the current position in the file.
58
     *
59
     * @return int
60
     */
61
    public function getCurrentOffset()
62
    {
63
        return $this->fileObject->ftell();
64
    }
65
66
    /**
67
     * Writes a raw data line to the stream.
68
     *
69
     * A newline character is appended after the data. Keep in mind that you may still be after a token which requires
70
     * a following whitespace, depending on the context you are in.
71
     *
72
     * @param string $data
73
     */
74
    public function writeRawLine($data)
75
    {
76
        $this->fileObject->fwrite($data . "\n");
77
    }
78
79
    /**
80
     * Writes raw data to the stream.
81
     *
82
     * @param string $data
83
     */
84
    public function writeRaw($data)
85
    {
86
        $this->fileObject->fwrite($data);
87
    }
88
89
    /**
90
     * Returns all object offsets.
91
     *
92
     * @return int
93
     */
94
    public function getObjectOffsets()
95
    {
96
        return $this->objectOffsets;
97
    }
98
99
    /**
100
     * Allocates a new ID for an object.
101
     *
102
     * @return int
103
     */
104
    public function allocateObjectId()
105
    {
106
        return ++$this->lastAllocatedObjectId;
107
    }
108
109
    /**
110
     * Starts an object.
111
     *
112
     * If the object ID is omitted, a new one is allocated.
113
     *
114
     * @param  int|null $objectId
115
     * @return int
116
     */
117
    public function startObject($objectId = null)
118
    {
119
        if (null === $objectId) {
120
            $objectId = ++$this->lastAllocatedObjectId;
121
        }
122
123
        $this->objectOffsets[$objectId] = $this->fileObject->ftell();
124
        $this->fileObject->fwrite(sprintf("%d 0 obj\n", $objectId));
125
126
        return $objectId;
127
    }
128
129
    /**
130
     * Ends an object.
131
     */
132
    public function endObject()
133
    {
134
        $this->fileObject->fwrite("\nendobj\n");
135
    }
136
137
    /**
138
     * Starts a stream.
139
     */
140
    public function startStream()
141
    {
142
        $this->fileObject->fwrite("stream\n");
143
    }
144
145
    public function endStream()
146
    {
147
        $this->fileObject->fwrite("\nendstream\n");
148
    }
149
150
    /**
151
     * Writes an indirect reference
152
     *
153
     * @param int $objectId
154
     */
155
    public function writeIndirectReference($objectId)
156
    {
157
        if ($this->requiresWhitespace) {
158
            $this->fileObject->fwrite(sprintf(' %d 0 R', $objectId));
159
        } else {
160
            $this->fileObject->fwrite(sprintf('%d 0 R', $objectId));
161
        }
162
163
        $this->requiresWhitespace = true;
164
    }
165
166
    /**
167
     * Starts a dictionary.
168
     */
169
    public function startDictionary()
170
    {
171
        $this->fileObject->fwrite('<<');
172
        $this->requiresWhitespace = false;
173
    }
174
175
    /**
176
     * Ends a dictionary.
177
     */
178
    public function endDictionary()
179
    {
180
        $this->fileObject->fwrite('>>');
181
        $this->requiresWhitespace = false;
182
    }
183
184
    /**
185
     * Starts an array.
186
     */
187
    public function startArray()
188
    {
189
        $this->fileObject->fwrite('[');
190
        $this->requiresWhitespace = false;
191
    }
192
193
    /**
194
     * Ends an array.
195
     */
196
    public function endArray()
197
    {
198
        $this->fileObject->fwrite(']');
199
        $this->requiresWhitespace = false;
200
    }
201
202
    /**
203
     * Writes a null value.
204
     */
205
    public function writeNull()
206
    {
207
        if ($this->requiresWhitespace) {
208
            $this->fileObject->fwrite(' null');
209
        } else {
210
            $this->fileObject->fwrite('null');
211
        }
212
213
        $this->requiresWhitespace = true;
214
    }
215
216
    /**
217
     * Writes a boolean.
218
     *
219
     * @param bool $boolean
220
     */
221
    public function writeBoolean($boolean)
222
    {
223
        if ($this->requiresWhitespace) {
224
            $this->fileObject->fwrite($boolean ? ' true' : ' false');
225
        } else {
226
            $this->fileObject->fwrite($boolean ? 'true' : 'false');
227
        }
228
229
        $this->requiresWhitespace = true;
230
    }
231
232
    /**
233
     * Writes a number.
234
     *
235
     * @param  int|float $number
236
     * @throws InvalidArgumentException
237
     */
238
    public function writeNumber($number)
239
    {
240
        if ($this->requiresWhitespace) {
241
            $this->fileObject->fwrite(' ' . (rtrim(sprintf('%.6F', $number), '0.') ?: '0'));
242
        } else {
243
            $this->fileObject->fwrite(rtrim(sprintf('%.6F', $number), '0.') ?: '0');
244
        }
245
246
        $this->requiresWhitespace = true;
247
    }
248
249
    /**
250
     * Writes a name.
251
     *
252
     * @param string $name
253
     */
254
    public function writeName($name)
255
    {
256
        $this->fileObject->fwrite('/' . $name);
257
        $this->requiresWhitespace = true;
258
    }
259
260
    /**
261
     * Writes a literal string.
262
     *
263
     * The string itself is splitted into multiple lines after 248 characters. We chose that specific limit to avoid
264
     * splitting mutli-byte characters in half.
265
     *
266
     * @param string $string
267
     */
268
    public function writeLiteralString($string)
269
    {
270
        $this->fileObject->fwrite('(' . strtr($string, ['(' => '\\(', ')' => '\\)', '\\' => '\\\\']) . ')');
271
        $this->requiresWhitespace = false;
272
    }
273
274
    /**
275
     * Writes a hexadecimal string.
276
     *
277
     * @param string $string
278
     */
279
    public function writeHexadecimalString($string)
280
    {
281
        $this->fileObject->fwrite('<' . bin2hex($string) . '>');
282
        $this->requiresWhitespace = false;
283
    }
284
}
285