Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
ParentObject
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
10 / 10
12
100.00% covered (success)
100.00%
1 / 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%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 addKid
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 removeKid
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setKids
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setCount
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getKids
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasKid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
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\PdfObject;
16
17/**
18 * Pdf parent object class
19 *
20 * @category   Pop
21 * @package    Pop\Pdf
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    6.0.0
26 */
27class ParentObject extends AbstractObject
28{
29
30    /**
31     * PDF parent object index
32     * @var ?int
33     */
34    protected ?int $index = 2;
35
36    /**
37     * PDF parent kids
38     * @var array
39     */
40    protected array $kids = [];
41
42    /**
43     * Explicit kid count override, used when kids are not leaf pages
44     * themselves (e.g. a merge's master node, whose kids are each source's
45     * own top-level Pages node) - null falls back to count($this->kids)
46     * @var ?int
47     */
48    protected ?int $countOverride = null;
49
50    /**
51     * Constructor
52     *
53     * Instantiate a PDF parent object.
54     *
55     * @param  int $index
56     */
57    public function __construct(int $index = 2)
58    {
59        $this->setIndex($index);
60        $this->setData("[{parent_index}] 0 obj\n<</Type/Pages/Count [{count}]/Kids[[{kids}]]>>\nendobj\n");
61    }
62
63    /**
64     * Parse a parent object from a string
65     *
66     * @param  string $stream
67     * @return ParentObject
68     */
69    public static function parse(string $stream): ParentObject
70    {
71        $parent = new self();
72
73        $parent->setIndex((int)substr($stream, 0, strpos($stream, ' ')));
74        $stream = str_replace($parent->getIndex() . ' 0 obj', '[{parent_index}] 0 obj', $stream);
75
76        // Determine the kids count.
77        $matches = [];
78        preg_match('/\/Count\s\d*/', $stream, $matches);
79        $count  = $matches[0];
80        $count  = str_replace('/Count ', '', $count);
81        $stream = str_replace('Count ' . $count, 'Count [{count}]', $stream);
82
83        // Determine the kids object indices.
84        $kids = trim(substr($stream, (strpos($stream, '/Kids') + 5)));
85        $kids = (str_starts_with($kids, '[')) ? substr($kids, 0, strpos($kids, ']') + 1) :
86            substr($kids, 0, (strpos($kids, ' R') + 2));
87
88        $kidIndices = $parent->getDictionaryReferences(substr($stream, (strpos($stream, '/Kids') + 5)));
89
90        $parent->setKids($kidIndices);
91        $parent->setData(str_replace($kids, '[[{kids}]]', $stream) . "\n");
92
93        return $parent;
94    }
95
96    /**
97     * Add a kid index to the parent object
98     *
99     * @param  int $kid
100     * @return ParentObject
101     */
102    public function addKid(int$kid): ParentObject
103    {
104        $this->kids[] = (int)$kid;
105        return $this;
106    }
107
108    /**
109     * Remove a kid index from the parent object
110     *
111     * @param  int $kid
112     * @return ParentObject
113     */
114    public function removeKid(int $kid): ParentObject
115    {
116        if ($this->hasKid($kid)) {
117            unset($this->kids[array_search($kid, $this->kids)]);
118        }
119        return $this;
120    }
121
122    /**
123     * Set the parent object kids
124     *
125     * @param  array $kids
126     * @return ParentObject
127     */
128    public function setKids(array $kids): ParentObject
129    {
130        $this->kids = $kids;
131        return $this;
132    }
133
134    /**
135     * Set an explicit kid count, overriding count($this->kids)
136     *
137     * @param  int $count
138     * @return ParentObject
139     */
140    public function setCount(int $count): ParentObject
141    {
142        $this->countOverride = $count;
143        return $this;
144    }
145
146    /**
147     * Get the parent object kid count
148     *
149     * @return int
150     */
151    public function getCount(): int
152    {
153        return $this->countOverride ?? count($this->kids);
154    }
155
156    /**
157     * Get the parent object kid indices
158     *
159     * @return array
160     */
161    public function getKids(): array
162    {
163        return $this->kids;
164    }
165
166    /**
167     * Determine whether the parent object contains a kid object index
168     *
169     * @param  int $kid
170     * @return bool
171     */
172    public function hasKid(int $kid): bool
173    {
174        return (in_array($kid, $this->kids));
175    }
176
177    /**
178     * Method to print the parent object.
179     *
180     * @return string
181     */
182    public function __toString(): string
183    {
184        return str_replace(['[{parent_index}]', '[{count}]', '[{kids}]'],
185            [(string)$this->index, (string)$this->getCount(), implode(" 0 R ", $this->kids) . " 0 R"], $this->data);
186    }
187
188}