Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
16 / 16
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractCompiler
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
16 / 16
22
100.00% covered (success)
100.00%
1 / 1
 getDocument
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRoot
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInfo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFonts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFontReferences
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getObjects
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 lastIndex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addObject
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRoot
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setParent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setInfo
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 calculateByteLength
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatByteLength
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCoordinates
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 setDocument
n/a
0 / 0
n/a
0 / 0
0
 finalize
n/a
0 / 0
n/a
0 / 0
0
 prepareFonts
n/a
0 / 0
n/a
0 / 0
0
 prepareImages
n/a
0 / 0
n/a
0 / 0
0
 prepareText
n/a
0 / 0
n/a
0 / 0
0
 prepareAnnotations
n/a
0 / 0
n/a
0 / 0
0
 preparePaths
n/a
0 / 0
n/a
0 / 0
0
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Pdf\Build;
16
17use Pop\Pdf\Document;
18
19/**
20 * Abstract Pdf compiler class
21 *
22 * @category   Pop
23 * @package    Pop\Pdf
24 * @author     Nick Sagona, III <nick@popphp.org>
25 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
26 * @license    https://www.popphp.org/license     New BSD License
27 * @version    6.0.0
28 */
29abstract class AbstractCompiler implements CompilerInterface
30{
31
32    /**
33     * Root object
34     * @var ?PdfObject\RootObject
35     */
36    protected ?PdfObject\RootObject $root = null;
37
38    /**
39     * Parent object
40     * @var ?PdfObject\ParentObject
41     */
42    protected ?PdfObject\ParentObject$parent = null;
43
44    /**
45     * Info object
46     * @var ?PdfObject\InfoObject
47     */
48    protected ?PdfObject\InfoObject$info = null;
49
50    /**
51     * Document object
52     * @var ?Document
53     */
54    protected ?Document $document = null;
55
56    /**
57     * Pages array
58     * @var array
59     */
60    protected array $pages = [];
61
62    /**
63     * Objects array
64     * @var array
65     */
66    protected array $objects = [];
67
68    /**
69     * Highest object index currently stored in $objects. Maintained
70     * incrementally (via addObject()) so that lastIndex() is O(1) instead
71     * of re-scanning/sorting the entire, ever-growing $objects array.
72     * @var int
73     */
74    protected int $lastIndex = 0;
75
76    /**
77     * Fonts array
78     * @var array
79     */
80    protected array $fonts = [];
81
82    /**
83     * Font references
84     * @var array
85     */
86    protected array $fontReferences = [];
87
88    /**
89     * Compression property
90     * @var bool
91     */
92    protected bool $compression = true;
93
94    /**
95     * PDF byte length
96     * @var ?int
97     */
98    protected ?int $byteLength = null;
99
100    /**
101     * PDF document trailer
102     * @var ?string
103     */
104    protected ?string $trailer = null;
105
106    /**
107     * PDF document output buffer
108     * @var ?string
109     */
110    protected ?string $output = null;
111
112    /**
113     * Get the document object
114     *
115     * @return ?Document
116     */
117    public function getDocument(): ?Document
118    {
119        return $this->document;
120    }
121
122    /**
123     * Get the root object
124     *
125     * @return ?PdfObject\RootObject
126     */
127    public function getRoot(): ?PdfObject\RootObject
128    {
129        return $this->root;
130    }
131
132    /**
133     * Get the parent object
134     *
135     * @return ?PdfObject\ParentObject
136     */
137    public function getParent(): ?PdfObject\ParentObject
138    {
139        return $this->parent;
140    }
141
142    /**
143     * Get the info object
144     *
145     * @return ?PdfObject\InfoObject
146     */
147    public function getInfo(): ?PdfObject\InfoObject
148    {
149        return $this->info;
150    }
151
152    /**
153     * Get the fonts
154     *
155     * @return array
156     */
157    public function getFonts(): array
158    {
159        return $this->fonts;
160    }
161
162    /**
163     * Get the font references
164     *
165     * @return array
166     */
167    public function getFontReferences(): array
168    {
169        return $this->fontReferences;
170    }
171
172    /**
173     * Get the compiled objects
174     *
175     * @return array
176     */
177    public function getObjects(): array
178    {
179        return $this->objects;
180    }
181
182    /**
183     * Return the last object index.
184     *
185     * @return int
186     */
187    public function lastIndex(): int
188    {
189        return $this->lastIndex;
190    }
191
192    /**
193     * Add an object to the objects array, keeping the cached last index
194     * (returned by lastIndex()) in sync. Objects are not guaranteed to be
195     * added in strictly ascending index order (e.g. imported/merged
196     * objects), so the cached index is updated via a running max rather
197     * than a blind increment.
198     *
199     * @param  int|string|null           $index
200     * @param  PdfObject\ObjectInterface $object
201     * @return AbstractCompiler
202     */
203    protected function addObject(int|string|null $index, PdfObject\ObjectInterface $object): AbstractCompiler
204    {
205        $this->objects[$index] = $object;
206        if (is_int($index) && $index > $this->lastIndex) {
207            $this->lastIndex = $index;
208        }
209        return $this;
210    }
211
212    /**
213     * Get the compiled output
214     *
215     * @return string
216     */
217    public function getOutput(): string
218    {
219        return $this->output;
220    }
221
222    /**
223     * Set the root object
224     *
225     * @param  PdfObject\RootObject $root
226     * @return AbstractCompiler
227     */
228    protected function setRoot(PdfObject\RootObject $root): AbstractCompiler
229    {
230        $this->root = $root;
231        $this->addObject($this->root->getIndex(), $this->root);
232        return $this;
233    }
234
235    /**
236     * Set the parent object
237     *
238     * @param  PdfObject\ParentObject $parent
239     * @return AbstractCompiler
240     */
241    protected function setParent(PdfObject\ParentObject $parent): AbstractCompiler
242    {
243        $this->parent = $parent;
244        $this->addObject($this->parent->getIndex(), $this->parent);
245        return $this;
246    }
247
248    /**
249     * Set the info object
250     *
251     * @param  PdfObject\InfoObject $info
252     * @return AbstractCompiler
253     */
254    protected function setInfo(PdfObject\InfoObject $info): AbstractCompiler
255    {
256        $this->info = $info;
257        $this->addObject($this->info->getIndex(), $this->info);
258        return $this;
259    }
260
261    /**
262     * Calculate byte length
263     *
264     * @param  \Stringable|string|null $string
265     * @return int
266     */
267    protected function calculateByteLength(\Stringable|string|null $string): int
268    {
269        return strlen((string)$string);
270    }
271
272    /**
273     * Format byte length
274     *
275     * @param  int|string $num
276     * @return string
277     */
278    protected function formatByteLength(int|string $num): string
279    {
280        return sprintf('%010d', $num);
281    }
282
283    /**
284     * Get coordinates based on document origin
285     *
286     * @param  int|float $x
287     * @param  int|float $y
288     * @param  PdfObject\PageObject $pageObject
289     * @return array
290     */
291    protected function getCoordinates(int|float $x, int|float $y, PdfObject\PageObject $pageObject): array
292    {
293        $coordinates = ['x' => $x, 'y' => $y];
294        $width       = $pageObject->getWidth();
295        $height      = $pageObject->getHeight();
296
297        switch ($this->document->getOrigin()) {
298            case \Pop\Pdf\Document::ORIGIN_TOP_LEFT:
299                $coordinates['y'] = $height - $y;
300                break;
301            case \Pop\Pdf\Document::ORIGIN_TOP_RIGHT:
302                $coordinates['x'] = $width - $x;
303                $coordinates['y'] = $height - $y;
304                break;
305            case \Pop\Pdf\Document::ORIGIN_BOTTOM_RIGHT:
306                $coordinates['x'] = $width - $x;
307                break;
308            case \Pop\Pdf\Document::ORIGIN_CENTER:
309                $coordinates['x'] = round($width / 2) + $x;
310                $coordinates['y'] = round($height / 2) + $y;
311                break;
312        }
313
314        return $coordinates;
315    }
316
317    /**
318     * Set the document object
319     *
320     * @param  Document $document
321     * @return Compiler
322     */
323    abstract public function setDocument(Document $document): Compiler;
324
325    /**
326     * Compile and finalize the PDF document
327     *
328     * @param  ?Document $document
329     * @return void
330     */
331    abstract public function finalize(?Document $document = null): void;
332
333    /**
334     * Prepare the font objects
335     *
336     * @return void
337     */
338    abstract public function prepareFonts(): void;
339
340    /**
341     * Prepare the image objects
342     *
343     * @param  array $images
344     * @param  PdfObject\PageObject $pageObject
345     * @return void
346     */
347    abstract protected function prepareImages(array $images, PdfObject\PageObject $pageObject): void;
348
349    /**
350     * Prepare the text objects
351     *
352     * @param  array $text
353     * @param  PdfObject\PageObject $pageObject
354     * @return void
355     */
356    abstract protected function prepareText(array $text, PdfObject\PageObject $pageObject): void;
357
358    /**
359     * Prepare the annotation objects
360     *
361     * @param  array $annotations
362     * @param  PdfObject\PageObject $pageObject
363     * @return void
364     */
365    abstract protected function prepareAnnotations(array $annotations, PdfObject\PageObject $pageObject): void;
366
367    /**
368     * Prepare the path objects
369     *
370     * @param  array $paths
371     * @param  PdfObject\PageObject $pageObject
372     * @return void
373     */
374    abstract protected function preparePaths(array $paths, PdfObject\PageObject $pageObject): void;
375
376}