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