Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.65% covered (success)
98.65%
73 / 74
94.44% covered (success)
94.44%
17 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
Document
98.65% covered (success)
98.65%
73 / 74
94.44% covered (success)
94.44%
17 / 18
40
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 addPage
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 addPages
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 createPage
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 copyPage
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 orderPages
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 deletePage
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 addFont
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 embedFont
88.89% covered (success)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
5.03
 setCurrentPage
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setCurrentFont
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 importObjects
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 importFonts
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 hasImportedObjects
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasImportedFonts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImportObjects
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImportedFonts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
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 */
14namespace Pop\Pdf;
15
16use Pop\Pdf\Document\AbstractDocument;
17use Pop\Pdf\Document\Page;
18use Pop\Pdf\Document\Font;
19use Pop\Pdf\Document\Metadata;
20use Pop\Pdf\Document\Exception;
21
22/**
23 * Pdf document class
24 *
25 * @category   Pop
26 * @package    Pop\Pdf
27 * @author     Nick Sagona, III <dev@nolainteractive.com>
28 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
29 * @license    http://www.popphp.org/license     New BSD License
30 * @version    5.0.0
31 */
32class Document extends AbstractDocument
33{
34
35    /**
36     * Imported objects
37     * @var array
38     */
39    protected array $importedObjects = [];
40
41    /**
42     * Imported fonts
43     * @var array
44     */
45    protected array $importedFonts = [];
46
47    /**
48     * Constructor
49     *
50     * Instantiate a PDF document
51     *
52     * @param  ?Page     $page
53     * @param  ?Metadata $metadata
54     */
55    public function __construct(?Page $page = null, ?Metadata $metadata = null)
56    {
57        if ($page !== null) {
58            $this->addPage($page);
59        }
60        $metadata = ($metadata !== null) ? $metadata : new Metadata();
61        $this->setMetadata($metadata);
62    }
63
64    /**
65     * Add a page to the PDF document
66     *
67     * @param  Page $page
68     * @return Document
69     */
70    public function addPage(Page $page): Document
71    {
72        $this->pages[]     = $page;
73        $this->currentPage = count($this->pages);
74        return $this;
75    }
76
77    /**
78     * Add pages to the PDF document
79     *
80     * @param  array $pages
81     * @return Document
82     */
83    public function addPages(array $pages): Document
84    {
85        foreach ($pages as $page) {
86            $this->addPage($page);
87        }
88        return $this;
89    }
90
91    /**
92     * Create and return a new page object, adding it to the PDF document
93     *
94     * @param  mixed $size
95     * @param  ?int  $height
96     * @throws Exception
97     * @return Page
98     */
99    public function createPage(mixed $size, ?int $height = null): Page
100    {
101        $page = new Page($size, $height);
102        $this->addPage($page);
103        return $page;
104    }
105
106    /**
107     * Copy and return a page of the PDF, adding it to the PDF document
108     *
109     * @param  int  $p
110     * @param  bool $preserveContent
111     * @throws Exception
112     * @return Page
113     */
114    public function copyPage(int $p, bool $preserveContent = true): Page
115    {
116        if (!isset($this->pages[$p - 1])) {
117            throw new Exception("Error: That page (" . $p . ") does not exist.");
118        }
119
120        $page = clone $this->pages[$p - 1];
121
122        if (!$preserveContent) {
123            $page->clearContent();
124        }
125
126        $this->addPage($page);
127        return $page;
128    }
129
130    /**
131     * Order the pages
132     *
133     * @param  array $pages
134     * @throws Exception
135     * @return Document
136     */
137    public function orderPages(array $pages): Document
138    {
139        $newOrder = [];
140
141        // Check if the numbers of pages passed equals the number of pages in the PDF.
142        if (count($pages) != count($this->pages)) {
143            throw new Exception('Error: The pages array passed does not contain the same number of pages as the PDF.');
144        }
145
146        // Make sure each page passed is within the PDF and not out of range.
147        foreach ($pages as $value) {
148            if (!array_key_exists(($value - 1), $this->pages)) {
149                throw new Exception('Error: The pages array passed contains a page that does not exist.');
150            }
151        }
152
153        // Set the new order of the page objects.
154        foreach ($pages as $value) {
155            $newOrder[] = $this->pages[$value - 1];
156        }
157
158        // Set the pages arrays to the new order.
159        $this->pages = $newOrder;
160        return $this;
161    }
162
163    /**
164     * Delete a page from the PDF document
165     *
166     * @param  int $p
167     * @throws Exception
168     * @return Document
169     */
170    public function deletePage(int $p): Document
171    {
172        if (!isset($this->pages[$p - 1])) {
173            throw new Exception("Error: That page (" . $p . ") does not exist.");
174        }
175
176        unset($this->pages[$p - 1]);
177
178        // Reset current page if current page was the one deleted
179        if ($this->currentPage == $p) {
180            $this->currentPage = (count($this->pages) > 0) ? (count($this->pages) - 1) : null;
181        }
182
183        return $this;
184    }
185
186    /**
187     * Add a font
188     *
189     * @param  Font|string $font
190     * @param  bool        $embedOverride
191     * @throws Exception
192     * @return Document
193     */
194    public function addFont(Font|string $font, bool $embedOverride = false): Document
195    {
196        if (is_string($font)) {
197            $font = new Font($font);
198        }
199        if (!$font->isStandard()) {
200            $this->embedFont($font, $embedOverride);
201        } else {
202            if (!array_key_exists($font->getName(), $this->fonts)) {
203                $this->fonts[$font->getName()] = $font;
204                $this->currentFont = $font->getName();
205            }
206        }
207
208        return $this;
209    }
210
211    /**
212     * Add a font
213     *
214     * @param  Font $font
215     * @param  bool $embedOverride
216     * @throws Exception
217     * @return Document
218     */
219    public function embedFont(Font $font, bool $embedOverride = false): Document
220    {
221        if (!$font->isEmbedded()) {
222            $this->addFont($font);
223        } else {
224            if (!$font->parser()->isEmbeddable() && !$embedOverride) {
225                throw new Exception('Error: The font license does not allow for it to be embedded.');
226            }
227
228            if (!array_key_exists($font->parser()->getFontName(), $this->fonts)) {
229                $font->parser()->setCompression($this->compression);
230                $this->fonts[$font->parser()->getFontName()] = $font;
231                $this->currentFont = $font->parser()->getFontName();
232            }
233        }
234
235        return $this;
236    }
237
238    /**
239     * Set the current page of the PDF document
240     *
241     * @param  int $p
242     * @throws Exception
243     * @return Document
244     */
245    public function setCurrentPage(int $p): Document
246    {
247        // Check if the page exists.
248        if (!isset($this->pages[$p - 1])) {
249            throw new Exception("Error: That page (" . $p . ") does not exist.");
250        }
251        $this->currentPage = $p;
252
253        return $this;
254    }
255
256    /**
257     * Set the current font of the PDF document
258     *
259     * @param  string $name
260     * @throws Exception
261     * @return Document
262     */
263    public function setCurrentFont(string $name): Document
264    {
265        // Check if the font exists.
266        if (!isset($this->fonts[$name])) {
267            throw new Exception("Error: The font '" . $name . "' has not been added to the PDF document.");
268        }
269        $this->currentFont = $name;
270
271        return $this;
272    }
273
274    /**
275     * Import objects into document
276     *
277     * @param  array $objects
278     * @return Document
279     */
280    public function importObjects(array $objects): Document
281    {
282        $this->importedObjects = $objects;
283        return $this;
284    }
285
286    /**
287     * Import fonts into document
288     *
289     * @param  array $fonts
290     * @return Document
291     */
292    public function importFonts(array $fonts): Document
293    {
294        foreach ($fonts as $font) {
295            $this->fonts[$font['name']] = $font;
296        }
297        $this->importedFonts = $fonts;
298        return $this;
299    }
300
301    /**
302     * Determine if the document has imported objects
303     *
304     * @return bool
305     */
306    public function hasImportedObjects(): bool
307    {
308        return (count($this->importedObjects) > 0);
309    }
310
311    /**
312     * Determine if the document has imported fonts
313     *
314     * @return bool
315     */
316    public function hasImportedFonts(): bool
317    {
318        return (count($this->importedFonts) > 0);
319    }
320
321    /**
322     * Get the imported objects
323     *
324     * @return array
325     */
326    public function getImportObjects(): array
327    {
328        return $this->importedObjects;
329    }
330
331    /**
332     * Get the import fonts
333     *
334     * @return array
335     */
336    public function getImportedFonts(): array
337    {
338        return $this->importedFonts;
339    }
340
341    /**
342     * Output the PDF document to string
343     *
344     * @return string
345     */
346    public function __toString(): string
347    {
348        $compiler = new Build\Compiler();
349        $compiler->finalize($this);
350        return $compiler->getOutput();
351    }
352
353}