Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
78.82% |
67 / 85 |
|
58.82% |
10 / 17 |
CRAP | |
0.00% |
0 / 1 |
StreamObject | |
78.82% |
67 / 85 |
|
58.82% |
10 / 17 |
66.09 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
parse | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
2 | |||
setDefinition | |
66.67% |
16 / 24 |
|
0.00% |
0 / 1 |
17.33 | |||
setStream | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
appendStream | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getDefinition | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getStream | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
encode | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
5 | |||
decode | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
isEncoded | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getEncoding | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setPalette | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isPalette | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isXObject | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getByteLength | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
calculateByteLength | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Pdf\Build\PdfObject; |
15 | |
16 | /** |
17 | * Pdf stream object class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Pdf |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 5.0.0 |
25 | */ |
26 | class StreamObject extends AbstractObject |
27 | { |
28 | |
29 | /** |
30 | * PDF stream object index |
31 | * @var ?int |
32 | */ |
33 | protected ?int $index = 5; |
34 | |
35 | /** |
36 | * PDF stream object definition |
37 | * @var ?string |
38 | */ |
39 | protected ?string $definition = null; |
40 | |
41 | /** |
42 | * PDF stream object stream |
43 | * @var ?string |
44 | */ |
45 | protected ?string $stream = null; |
46 | |
47 | /** |
48 | * Encoding filter |
49 | * @var ?string |
50 | */ |
51 | protected ?string $encoding = null; |
52 | |
53 | /** |
54 | * Palette object flag |
55 | * @var bool |
56 | */ |
57 | protected bool $isPalette = false; |
58 | |
59 | /** |
60 | * XObject object flag |
61 | * @var bool |
62 | */ |
63 | protected bool $isXObject = false; |
64 | |
65 | /** |
66 | * Constructor |
67 | * |
68 | * Instantiate a PDF stream object. |
69 | * |
70 | * @param int $index |
71 | */ |
72 | public function __construct(int $index = 5) |
73 | { |
74 | $this->setIndex($index); |
75 | $this->setData("\n[{object_index}] 0 obj\n[{definition}]\n[{stream}]\nendobj\n\n"); |
76 | } |
77 | |
78 | /** |
79 | * Parse a stream object from a string |
80 | * |
81 | * @param string $stream |
82 | * @return StreamObject |
83 | */ |
84 | public static function parse(string $stream): StreamObject |
85 | { |
86 | $object = new self(); |
87 | $object->setIndex(substr($stream, 0, strpos($stream, ' '))); |
88 | $stream = str_replace($object->getIndex() . ' 0 obj', '[{object_index}] 0 obj', $stream); |
89 | |
90 | // Determine the objects definition and stream, if applicable. |
91 | $s = substr($stream, (strpos($stream, ' obj') + 4)); |
92 | $s = substr($s, 0, strpos($s, 'endobj')); |
93 | if (str_contains($s, 'stream')) { |
94 | $def = substr($s, 0, strpos($s, 'stream')); |
95 | $str = substr($s, (strpos($s, 'stream') + 6)); |
96 | $str = substr($str, 0, strpos($str, 'endstream')); |
97 | $object->setDefinition($def); |
98 | $object->appendStream($str); |
99 | } else { |
100 | $object->setDefinition($s); |
101 | } |
102 | |
103 | $object->setData("\n[{object_index}] 0 obj\n[{definition}]\n[{stream}]\nendobj\n\n"); |
104 | return $object; |
105 | } |
106 | |
107 | /** |
108 | * Set the stream object definition |
109 | * |
110 | * @param string $definition |
111 | * @return StreamObject |
112 | */ |
113 | public function setDefinition(string $definition): StreamObject |
114 | { |
115 | $this->definition = (string)$definition; |
116 | |
117 | if (str_contains($this->definition, '/ASCIIHexDecode')) { |
118 | $this->encoding = 'ASCIIHexDecode'; |
119 | } else if (str_contains($this->definition, '/ASCII85Decode')) { |
120 | $this->encoding = 'ASCII85Decode'; |
121 | } else if (str_contains($this->definition, '/LZWDecode')) { |
122 | $this->encoding = 'LZWDecode'; |
123 | } else if (str_contains($this->definition, '/FlateDecode')) { |
124 | $this->encoding = 'FlateDecode'; |
125 | } else if (str_contains($this->definition, '/RunLengthDecode')) { |
126 | $this->encoding = 'RunLengthDecode'; |
127 | } else if (str_contains($this->definition, '/CCITTFaxDecode')) { |
128 | $this->encoding = 'CCITTFaxDecode'; |
129 | } else if (str_contains($this->definition, '/JBIG2Decode')) { |
130 | $this->encoding = 'JBIG2Decode'; |
131 | } else if (str_contains($this->definition, '/DCTDecode')) { |
132 | $this->encoding = 'DCTDecode'; |
133 | } else if (str_contains($this->definition, '/JPXDecode')) { |
134 | $this->encoding = 'JPXDecode'; |
135 | } else if (str_contains($this->definition, '/Crypt')) { |
136 | $this->encoding = 'Crypt'; |
137 | } |
138 | |
139 | if (stripos($this->definition, '/xobject') !== false) { |
140 | $this->isXObject = true; |
141 | } |
142 | |
143 | return $this; |
144 | } |
145 | |
146 | /** |
147 | * Set the stream object stream |
148 | * |
149 | * @param string $stream |
150 | * @return StreamObject |
151 | */ |
152 | public function setStream(string $stream): StreamObject |
153 | { |
154 | $this->stream = $stream; |
155 | return $this; |
156 | } |
157 | |
158 | /** |
159 | * Append to the stream the PDF stream object |
160 | * |
161 | * @param string $stream |
162 | * @return StreamObject |
163 | */ |
164 | public function appendStream(string $stream): StreamObject |
165 | { |
166 | $this->stream .= $stream; |
167 | return $this; |
168 | } |
169 | |
170 | /** |
171 | * Get the stream object definition |
172 | * |
173 | * @return ?string |
174 | */ |
175 | public function getDefinition(): ?string |
176 | { |
177 | return $this->definition; |
178 | } |
179 | |
180 | /** |
181 | * Get the PDF stream object stream |
182 | * |
183 | * @return ?string |
184 | */ |
185 | public function getStream(): ?string |
186 | { |
187 | return $this->stream; |
188 | } |
189 | |
190 | /** |
191 | * Method to encode the PDF stream object with FlateDecode (gzcompress) |
192 | * |
193 | * @return void |
194 | */ |
195 | public function encode(): void |
196 | { |
197 | if (($this->stream != '') && (function_exists('gzcompress')) && |
198 | (!str_contains((string)$this->definition, ' /Image')) && (!str_contains((string)$this->definition, '/FlateDecode'))) { |
199 | $this->stream = "\n" . gzcompress($this->stream, 9) . "\n"; |
200 | $this->encoding = 'FlateDecode'; |
201 | } |
202 | } |
203 | |
204 | /** |
205 | * Method to decode the PDF stream contents with FlateDecode (gzuncompress) |
206 | * |
207 | * @return bool|string |
208 | */ |
209 | public function decode(): bool|string |
210 | { |
211 | $decoded = false; |
212 | if (($this->stream != '') && function_exists('gzuncompress')) { |
213 | $decoded = @gzuncompress(trim($this->stream)); |
214 | } |
215 | return $decoded; |
216 | } |
217 | |
218 | /** |
219 | * Determine whether or not the PDF stream object is encoded |
220 | * |
221 | * @return bool |
222 | */ |
223 | public function isEncoded(): bool |
224 | { |
225 | return ($this->encoding !== null); |
226 | } |
227 | |
228 | /** |
229 | * Get the encoding filter |
230 | * |
231 | * @return ?string |
232 | */ |
233 | public function getEncoding(): ?string |
234 | { |
235 | return $this->encoding; |
236 | } |
237 | |
238 | /** |
239 | * Set whether the PDF stream object is a palette object |
240 | * |
241 | * @param bool $isPalette |
242 | * @return StreamObject |
243 | */ |
244 | public function setPalette(bool $isPalette): StreamObject |
245 | { |
246 | $this->isPalette = $isPalette; |
247 | return $this; |
248 | } |
249 | |
250 | /** |
251 | * Get whether the PDF stream object is a palette object |
252 | * |
253 | * @return bool |
254 | */ |
255 | public function isPalette(): bool |
256 | { |
257 | return $this->isPalette; |
258 | } |
259 | |
260 | /** |
261 | * Get whether the PDF stream object is an XObject |
262 | * |
263 | * @return bool |
264 | */ |
265 | public function isXObject(): bool |
266 | { |
267 | return $this->isXObject; |
268 | } |
269 | |
270 | /** |
271 | * Get the PDF stream object byte length |
272 | * |
273 | * @return int |
274 | */ |
275 | public function getByteLength(): int |
276 | { |
277 | return $this->calculateByteLength((string)$this); |
278 | } |
279 | |
280 | /** |
281 | * Calculate the byte length of a string |
282 | * |
283 | * @param ?string $string |
284 | * @return int |
285 | */ |
286 | protected function calculateByteLength(?string $string): int |
287 | { |
288 | return strlen((string)$string); |
289 | } |
290 | |
291 | /** |
292 | * Method to print the PDF stream object. |
293 | * |
294 | * @return string |
295 | */ |
296 | public function __toString(): string |
297 | { |
298 | // Set the stream. |
299 | $stream = ($this->stream !== null) ? "stream" . $this->stream . "endstream\n" : ''; |
300 | |
301 | // Set up the Length definition. |
302 | if ((str_contains((string)$this->definition, '/Length ')) && (!str_contains((string)$this->definition, '/Length1')) && |
303 | (!str_contains((string)$this->definition, '/Image'))) { |
304 | $matches = []; |
305 | preg_match('/\/Length\s\d*/', $this->definition, $matches); |
306 | if (isset($matches[0])) { |
307 | $len = $matches[0]; |
308 | $len = str_replace('/Length', '', $len); |
309 | $len = str_replace(' ', '', $len); |
310 | $this->definition = str_replace($len, '[{byte_length}]', $this->definition); |
311 | } |
312 | } else if (!str_contains((string)$this->definition, '/Length')) { |
313 | $this->definition .= "<</Length [{byte_length}]>>\n"; |
314 | } |
315 | |
316 | // Calculate the byte length of the stream and swap out the placeholders. |
317 | $byteLength = (($this->encoding == 'FlateDecode') && (function_exists('gzcompress')) && |
318 | (!str_contains((string)$this->definition, ' /Image')) && (!str_contains((string)$this->definition, '/FlateDecode'))) ? |
319 | $this->calculateByteLength($this->stream) . " /Filter /FlateDecode" : $this->calculateByteLength($this->stream); |
320 | |
321 | $data = str_replace( |
322 | ['[{object_index}]', '[{stream}]', '[{definition}]', '[{byte_length}]'], |
323 | [$this->index, $stream, $this->definition, $byteLength], |
324 | $this->data |
325 | ); |
326 | |
327 | // Clear Length definition if it is zero. |
328 | if (str_contains((string)$data, '<</Length 0>>')) { |
329 | $data = str_replace('<</Length 0>>', '', $data); |
330 | } |
331 | |
332 | return $data; |
333 | } |
334 | |
335 | } |