22
33namespace setasign \SetaPDF2 \Demos \Stamper \Stamp ;
44
5+ use setasign \SetaPDF2 \Core \DataStructure \Tree \NameTree ;
56use setasign \SetaPDF2 \Core \DataStructure \Tree \NumberTree ;
67use setasign \SetaPDF2 \Core \Document ;
78use setasign \SetaPDF2 \Core \Document \Action \Action ;
89use setasign \SetaPDF2 \Core \Document \OptionalContent \Group ;
910use setasign \SetaPDF2 \Core \Document \Page ;
1011use setasign \SetaPDF2 \Core \Encoding \Encoding ;
12+ use setasign \SetaPDF2 \Core \Type \Dictionary \DictionaryHelper ;
1113use setasign \SetaPDF2 \Core \Type \PdfArray ;
1214use setasign \SetaPDF2 \Core \Type \PdfDictionary ;
15+ use setasign \SetaPDF2 \Core \Type \PdfIndirectReference ;
1316use setasign \SetaPDF2 \Core \Type \PdfName ;
1417use setasign \SetaPDF2 \Core \Type \PdfNumeric ;
1518use setasign \SetaPDF2 \Core \Type \PdfString ;
19+ use setasign \SetaPDF2 \NotImplementedException ;
1620use setasign \SetaPDF2 \Stamper \Stamp \AbstractStamp ;
1721
1822/**
@@ -25,26 +29,31 @@ class Tagged extends AbstractStamp
2529 */
2630 protected $ _mainStamp ;
2731
32+ protected $ _parentId ;
2833 protected $ _tagName = 'Span ' ;
2934 protected $ _title = '' ;
3035 protected $ _actualText = '' ;
3136 protected $ _alternateText = '' ;
3237 protected $ _language = '' ;
38+ protected $ _stampedOnPage ;
3339
3440 /**
3541 * The constructor
3642 *
3743 * @param AbstractStamp $mainStamp The main stamp instance
44+ * @param string|null $parentId The ID of the parent structure element. If set to null the new tag
45+ * will be added to the root of the structure tree.
3846 */
39- public function __construct (AbstractStamp $ mainStamp )
47+ public function __construct (AbstractStamp $ mainStamp, ? string $ parentId = null )
4048 {
4149 $ this ->_mainStamp = $ mainStamp ;
50+ $ this ->_parentId = $ parentId ;
4251 }
4352
4453 /**
45- * @param string $tagName
54+ * @param ? string $tagName
4655 */
47- public function setTagName (string $ tagName )
56+ public function setTagName (? string $ tagName )
4857 {
4958 $ this ->_tagName = $ tagName ;
5059 }
@@ -109,34 +118,78 @@ protected function _stamp(Document $document, Page $page, array $stampData)
109118 $ structTreeRoot = $ document ->getCatalog ()->getStructTreeRoot ();
110119 $ structTreeRoot ->getDictionary (true );
111120
112- $ pageDict = $ page ->getObject ()-> ensure ( );
121+ $ pageDict = PdfDictionary:: ensureType ( $ page ->getObject ());
113122 if (!$ pageDict ->offsetExists ('StructParents ' )) {
114123 $ pageDict ->offsetSet (
115124 'StructParents ' ,
116125 new PdfNumeric ($ structTreeRoot ->getAndIncrementParentTreeNextKey ())
117126 );
118127 }
119128
120- $ structParentsKey = $ pageDict ->getValue ('StructParents ' )->getValue ();
129+ $ structParentsKey = PdfNumeric:: ensureType ( $ pageDict ->getValue ('StructParents ' ) )->getValue ();
121130
122131 /** @var NumberTree $parentTree */
123132 $ parentTree = $ structTreeRoot ->getParentTree (true );
124- $ elements = $ parentTree ->get ($ structParentsKey );
125- if ($ elements !== false ) {
126- $ elements = $ elements ->ensure ();
133+ $ parentElements = $ parentTree ->get ($ structParentsKey );
134+ if ($ parentElements !== false ) {
135+ $ parentElements = $ parentElements ->ensure ();
127136 } else {
128- $ elements = new PdfArray ();
129- $ parentTree ->add ($ structParentsKey , $ document ->createNewObject ($ elements ));
137+ $ parentElements = new PdfArray ();
138+ $ parentTree ->add ($ structParentsKey , $ document ->createNewObject ($ parentElements ));
130139 }
131140
132- $ mcid = count ($ elements );
141+ $ mcid = \count ($ parentElements );
142+
143+ if ($ this ->_parentId !== null ) {
144+ $ idTree = $ structTreeRoot ->getIdTree ();
145+ if (!$ idTree instanceof NameTree) {
146+ throw new \InvalidArgumentException (\sprintf (
147+ 'The parentId (%s) cannot be found. ' ,
148+ $ this ->_parentId
149+ ));
150+ }
151+
152+ $ parent = $ idTree ->get ($ this ->_parentId );
153+ if (!$ parent instanceof PdfIndirectReference) {
154+ throw new \InvalidArgumentException (\sprintf (
155+ 'The parentId (%s) cannot be found. ' ,
156+ $ this ->_parentId
157+ ));
158+ }
159+ } else {
160+ $ parent = $ structTreeRoot ->getObject ();
161+ }
133162
134- $ element = new PdfDictionary ([
135- 'K ' => new PdfNumeric ($ mcid ),
136- 'P ' => $ structTreeRoot ->getObject (),
137- 'Pg ' => $ page ->getObject (),
138- 'S ' => new PdfName ($ this ->_tagName , true )
139- ]);
163+ if ($ this ->_tagName === null ) {
164+ if ($ this ->_parentId === null ) {
165+ throw new \InvalidArgumentException (
166+ 'The tagName can only be left, if a parentId is provided. '
167+ );
168+ }
169+
170+ $ element = PdfDictionary::ensureType ($ parent );
171+ $ parentElements [] = $ parent ;
172+ $ newKidValue = new PdfNumeric ($ mcid );
173+ } else {
174+ $ element = new PdfDictionary ([
175+ 'K ' => new PdfNumeric ($ mcid ),
176+ 'P ' => $ parent ,
177+ 'S ' => new PdfName ($ this ->_tagName , true )
178+ ]);
179+
180+ $ newKidValue = $ document ->createNewObject ($ element );
181+ $ parentElements [] = $ newKidValue ;
182+ }
183+
184+ if ($ this ->_tagName === null ) {
185+ $ pageObjectId = $ page ->getObject ()->getObjectId ();
186+ if ($ this ->_stampedOnPage !== null && $ this ->_stampedOnPage !== $ pageObjectId ) {
187+ throw new \InvalidArgumentException ('A stamp without a tag-name can only be stamped on a single page. ' );
188+ }
189+
190+ $ element ['Pg ' ] = $ page ->getObject ();
191+ $ this ->_stampedOnPage = $ pageObjectId ;
192+ }
140193
141194 if ($ this ->_title !== '' ) {
142195 $ element ->offsetSet ('T ' , new PdfString (
@@ -162,18 +215,26 @@ protected function _stamp(Document $document, Page $page, array $stampData)
162215 ));
163216 }
164217
165- $ elementReference = $ document ->createNewObject ($ element );
218+ $ parentDict = PdfDictionary::ensureType ($ parent );
219+ $ k = DictionaryHelper::getValue ($ parentDict , 'K ' );
220+ if ($ k === null ) {
221+ $ k = new PdfArray ();
222+ $ parentDict ->offsetSet ('K ' , $ document ->createNewObject ($ k ));
223+ }
166224
167- $ elements [] = $ elementReference ;
225+ if (!$ k instanceof PdfArray) {
226+ $ k = new PdfArray ([$ k ]);
227+ $ parentDict ->offsetSet ('K ' , $ document ->createNewObject ($ k ));
228+ }
168229
169- $ structTreeRoot -> addChild ( $ elementReference ) ;
230+ $ k [] = $ newKidValue ;
170231
171232 $ canvas = $ page ->getCanvas ();
172233
173234 $ properties = new PdfDictionary ([
174235 'MCID ' => new PdfNumeric ($ mcid )
175236 ]);
176- $ canvas ->markedContent ()->begin ($ this ->_tagName , $ properties );
237+ $ canvas ->markedContent ()->begin ($ this ->_tagName ?? ' Span ' , $ properties );
177238
178239 $ this ->_mainStamp ->_stamp ($ document , $ page , $ stampData );
179240
@@ -245,7 +306,7 @@ public function getVisibility()
245306 */
246307 public function setAction (Action $ action )
247308 {
248- $ this -> _mainStamp -> setAction ( $ action );
309+ throw new NotImplementedException ( ' Actions are actually not implemented for tagged stamps. ' );
249310 }
250311
251312 /**
@@ -261,7 +322,7 @@ public function getAction()
261322 */
262323 public function setLink ($ uri )
263324 {
264- $ this -> _mainStamp -> setLink ( $ uri );
325+ throw new NotImplementedException ( ' Links are actually not implemented for tagged stamps. ' );
265326 }
266327
267328 /**
0 commit comments