Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.31% covered (success)
78.31%
65 / 83
58.82% covered (warning)
58.82%
10 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
StreamObject
78.31% covered (success)
78.31%
65 / 83
58.82% covered (warning)
58.82%
10 / 17
67.58
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 parse
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
 setDefinition
66.67% covered (warning)
66.67%
16 / 24
0.00% covered (danger)
0.00%
0 / 1
17.33
 setStream
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 appendStream
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDefinition
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStream
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 encode
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
5
 decode
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 isEncoded
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEncoding
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPalette
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isPalette
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isXObject
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getByteLength
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 calculateByteLength
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
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-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace 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-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    4.2.0
25 */
26class StreamObject extends AbstractObject
27{
28
29    /**
30     * PDF stream object index
31     * @var int
32     */
33    protected $index = 5;
34
35    /**
36     * PDF stream object definition
37     * @var string
38     */
39    protected $definition = null;
40
41    /**
42     * PDF stream object stream
43     * @var string
44     */
45    protected $stream = null;
46
47    /**
48     * Encoding filter
49     * @var boolean
50     */
51    protected $encoding = null;
52
53    /**
54     * Palette object flag
55     * @var boolean
56     */
57    protected $isPalette = false;
58
59    /**
60     * XObject object flag
61     * @var boolean
62     */
63    protected $isXObject = false;
64
65    /**
66     * Constructor
67     *
68     * Instantiate a PDF stream object.
69     *
70     * @param  int $index
71     */
72    public function __construct($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($stream)
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 (strpos($s, 'stream') !== false) {
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($definition)
114    {
115        $this->definition = (string)$definition;
116
117        if (strpos($this->definition, '/ASCIIHexDecode') !== false) {
118            $this->encoding = 'ASCIIHexDecode';
119        } else if (strpos($this->definition, '/ASCII85Decode') !== false) {
120            $this->encoding = 'ASCII85Decode';
121        } else if (strpos($this->definition, '/LZWDecode') !== false) {
122            $this->encoding = 'LZWDecode';
123        } else if (strpos($this->definition, '/FlateDecode') !== false) {
124            $this->encoding = 'FlateDecode';
125        } else if (strpos($this->definition, '/RunLengthDecode') !== false) {
126            $this->encoding = 'RunLengthDecode';
127        } else if (strpos($this->definition, '/CCITTFaxDecode') !== false) {
128            $this->encoding = 'CCITTFaxDecode';
129        } else if (strpos($this->definition, '/JBIG2Decode') !== false) {
130            $this->encoding = 'JBIG2Decode';
131        } else if (strpos($this->definition, '/DCTDecode') !== false) {
132            $this->encoding = 'DCTDecode';
133        } else if (strpos($this->definition, '/JPXDecode') !== false) {
134            $this->encoding = 'JPXDecode';
135        } else if (strpos($this->definition, '/Crypt') !== false) {
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($stream)
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($stream)
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()
176    {
177        return $this->definition;
178    }
179
180    /**
181     * Get the PDF stream object stream
182     *
183     * @return string
184     */
185    public function getStream()
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()
196    {
197        if (($this->stream != '') && (function_exists('gzcompress')) &&
198            (strpos((string)$this->definition, ' /Image') === false) && (strpos((string)$this->definition, '/FlateDecode') === false)) {
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 boolean|string
208     */
209    public function decode()
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 boolean
222     */
223    public function isEncoded()
224    {
225        return (null !== $this->encoding);
226    }
227
228    /**
229     * Get the encoding filter
230     *
231     * @return string
232     */
233    public function getEncoding()
234    {
235        return $this->encoding;
236    }
237
238    /**
239     * Set whether the PDF stream object is a palette object
240     *
241     * @param  boolean $isPalette
242     * @return void
243     */
244    public function setPalette($isPalette)
245    {
246        $this->isPalette = (bool)$isPalette;
247    }
248
249    /**
250     * Get whether the PDF stream object is a palette object
251     *
252     * @return boolean
253     */
254    public function isPalette()
255    {
256        return $this->isPalette;
257    }
258
259    /**
260     * Get whether the PDF stream object is an XObject
261     *
262     * @return boolean
263     */
264    public function isXObject()
265    {
266        return $this->isXObject;
267    }
268
269    /**
270     * Get the PDF stream object byte length
271     *
272     * @return int
273     */
274    public function getByteLength()
275    {
276        return $this->calculateByteLength($this);
277    }
278
279    /**
280     * Calculate the byte length of a string
281     *
282     * @param  string $string
283     * @return int
284     */
285    protected function calculateByteLength($string)
286    {
287        return strlen((string)$string);
288    }
289
290    /**
291     * Method to print the PDF stream object.
292     *
293     * @return string
294     */
295    public function __toString()
296    {
297        // Set the stream.
298        $stream = (null !== $this->stream) ? "stream" . $this->stream . "endstream\n" : '';
299
300        // Set up the Length definition.
301        if ((strpos((string)$this->definition, '/Length ') !== false) && (strpos((string)$this->definition, '/Length1') === false) &&
302            (strpos((string)$this->definition, '/Image') === false)) {
303            $matches = [];
304            preg_match('/\/Length\s\d*/', $this->definition, $matches);
305            if (isset($matches[0])) {
306                $len = $matches[0];
307                $len = str_replace('/Length', '', $len);
308                $len = str_replace(' ', '', $len);
309                $this->definition = str_replace($len, '[{byte_length}]', $this->definition);
310            }
311        } else if (strpos((string)$this->definition, '/Length') === false) {
312            $this->definition .= "<</Length [{byte_length}]>>\n";
313        }
314
315        // Calculate the byte length of the stream and swap out the placeholders.
316        $byteLength = (($this->encoding == 'FlateDecode') && (function_exists('gzcompress')) &&
317            (strpos((string)$this->definition, ' /Image') === false) && (strpos((string)$this->definition, '/FlateDecode') === false)) ?
318            $this->calculateByteLength($this->stream) . " /Filter /FlateDecode" : $this->calculateByteLength($this->stream);
319
320        $data = str_replace(
321            ['[{object_index}]', '[{stream}]', '[{definition}]', '[{byte_length}]'],
322            [$this->index, $stream, $this->definition, $byteLength],
323            $this->data
324        );
325
326        // Clear Length definition if it is zero.
327        if (strpos((string)$data, '<</Length 0>>') !== false) {
328            $data = str_replace('<</Length 0>>', '', $data);
329        }
330
331        return $data;
332    }
333
334}