|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Pdf |
|
17 * @subpackage Actions |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: Action.php 22437 2010-06-15 16:13:46Z alexander $ |
|
21 */ |
|
22 |
|
23 |
|
24 /** Internally used classes */ |
|
25 require_once 'Zend/Pdf/Element.php'; |
|
26 require_once 'Zend/Pdf/Element/Array.php'; |
|
27 |
|
28 |
|
29 /** Zend_Pdf_Target */ |
|
30 require_once 'Zend/Pdf/Target.php'; |
|
31 |
|
32 |
|
33 /** |
|
34 * Abstract PDF action representation class |
|
35 * |
|
36 * @package Zend_Pdf |
|
37 * @subpackage Actions |
|
38 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
39 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
40 */ |
|
41 abstract class Zend_Pdf_Action extends Zend_Pdf_Target implements RecursiveIterator, Countable |
|
42 { |
|
43 /** |
|
44 * Action dictionary |
|
45 * |
|
46 * @var Zend_Pdf_Element_Dictionary|Zend_Pdf_Element_Object|Zend_Pdf_Element_Reference |
|
47 */ |
|
48 protected $_actionDictionary; |
|
49 |
|
50 |
|
51 /** |
|
52 * An original list of chained actions |
|
53 * |
|
54 * @var array Array of Zend_Pdf_Action objects |
|
55 */ |
|
56 protected $_originalNextList; |
|
57 |
|
58 /** |
|
59 * A list of next actions in actions tree (used for actions chaining) |
|
60 * |
|
61 * @var array Array of Zend_Pdf_Action objects |
|
62 */ |
|
63 public $next = array(); |
|
64 |
|
65 /** |
|
66 * Object constructor |
|
67 * |
|
68 * @param Zend_Pdf_Element_Dictionary $dictionary |
|
69 * @param SplObjectStorage $processedActions list of already processed action dictionaries, used to avoid cyclic references |
|
70 * @throws Zend_Pdf_Exception |
|
71 */ |
|
72 public function __construct(Zend_Pdf_Element $dictionary, SplObjectStorage $processedActions) |
|
73 { |
|
74 require_once 'Zend/Pdf/Element.php'; |
|
75 if ($dictionary->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) { |
|
76 require_once 'Zend/Pdf/Exception.php'; |
|
77 throw new Zend_Pdf_Exception('$dictionary mast be a direct or an indirect dictionary object.'); |
|
78 } |
|
79 |
|
80 $this->_actionDictionary = $dictionary; |
|
81 |
|
82 if ($dictionary->Next !== null) { |
|
83 if ($dictionary->Next instanceof Zend_Pdf_Element_Dictionary) { |
|
84 // Check if dictionary object is not already processed |
|
85 if (!$processedActions->contains($dictionary->Next)) { |
|
86 $processedActions->attach($dictionary->Next); |
|
87 $this->next[] = Zend_Pdf_Action::load($dictionary->Next, $processedActions); |
|
88 } |
|
89 } else if ($dictionary->Next instanceof Zend_Pdf_Element_Array) { |
|
90 foreach ($dictionary->Next->items as $chainedActionDictionary) { |
|
91 // Check if dictionary object is not already processed |
|
92 if (!$processedActions->contains($chainedActionDictionary)) { |
|
93 $processedActions->attach($chainedActionDictionary); |
|
94 $this->next[] = Zend_Pdf_Action::load($chainedActionDictionary, $processedActions); |
|
95 } |
|
96 } |
|
97 } else { |
|
98 require_once 'Zend/Pdf/Exception.php'; |
|
99 throw new Zend_Pdf_Exception('PDF Action dictionary Next entry must be a dictionary or an array.'); |
|
100 } |
|
101 } |
|
102 |
|
103 $this->_originalNextList = $this->next; |
|
104 } |
|
105 |
|
106 /** |
|
107 * Load PDF action object using specified dictionary |
|
108 * |
|
109 * @internal |
|
110 * @param Zend_Pdf_Element $dictionary (It's actually Dictionary or Dictionary Object or Reference to a Dictionary Object) |
|
111 * @param SplObjectStorage $processedActions list of already processed action dictionaries, used to avoid cyclic references |
|
112 * @return Zend_Pdf_Action |
|
113 * @throws Zend_Pdf_Exception |
|
114 */ |
|
115 public static function load(Zend_Pdf_Element $dictionary, SplObjectStorage $processedActions = null) |
|
116 { |
|
117 if ($processedActions === null) { |
|
118 $processedActions = new SplObjectStorage(); |
|
119 } |
|
120 |
|
121 require_once 'Zend/Pdf/Element.php'; |
|
122 if ($dictionary->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) { |
|
123 require_once 'Zend/Pdf/Exception.php'; |
|
124 throw new Zend_Pdf_Exception('$dictionary mast be a direct or an indirect dictionary object.'); |
|
125 } |
|
126 if (isset($dictionary->Type) && $dictionary->Type->value != 'Action') { |
|
127 require_once 'Zend/Pdf/Exception.php'; |
|
128 throw new Zend_Pdf_Exception('Action dictionary Type entry must be set to \'Action\'.'); |
|
129 } |
|
130 |
|
131 if ($dictionary->S === null) { |
|
132 require_once 'Zend/Pdf/Exception.php'; |
|
133 throw new Zend_Pdf_Exception('Action dictionary must contain S entry'); |
|
134 } |
|
135 |
|
136 switch ($dictionary->S->value) { |
|
137 case 'GoTo': |
|
138 require_once 'Zend/Pdf/Action/GoTo.php'; |
|
139 return new Zend_Pdf_Action_GoTo($dictionary, $processedActions); |
|
140 break; |
|
141 |
|
142 case 'GoToR': |
|
143 require_once 'Zend/Pdf/Action/GoToR.php'; |
|
144 return new Zend_Pdf_Action_GoToR($dictionary, $processedActions); |
|
145 break; |
|
146 |
|
147 case 'GoToE': |
|
148 require_once 'Zend/Pdf/Action/GoToE.php'; |
|
149 return new Zend_Pdf_Action_GoToE($dictionary, $processedActions); |
|
150 break; |
|
151 |
|
152 case 'Launch': |
|
153 require_once 'Zend/Pdf/Action/Launch.php'; |
|
154 return new Zend_Pdf_Action_Launch($dictionary, $processedActions); |
|
155 break; |
|
156 |
|
157 case 'Thread': |
|
158 require_once 'Zend/Pdf/Action/Thread.php'; |
|
159 return new Zend_Pdf_Action_Thread($dictionary, $processedActions); |
|
160 break; |
|
161 |
|
162 case 'URI': |
|
163 require_once 'Zend/Pdf/Action/URI.php'; |
|
164 return new Zend_Pdf_Action_URI($dictionary, $processedActions); |
|
165 break; |
|
166 |
|
167 case 'Sound': |
|
168 require_once 'Zend/Pdf/Action/Sound.php'; |
|
169 return new Zend_Pdf_Action_Sound($dictionary, $processedActions); |
|
170 break; |
|
171 |
|
172 case 'Movie': |
|
173 require_once 'Zend/Pdf/Action/Movie.php'; |
|
174 return new Zend_Pdf_Action_Movie($dictionary, $processedActions); |
|
175 break; |
|
176 |
|
177 case 'Hide': |
|
178 require_once 'Zend/Pdf/Action/Hide.php'; |
|
179 return new Zend_Pdf_Action_Hide($dictionary, $processedActions); |
|
180 break; |
|
181 |
|
182 case 'Named': |
|
183 require_once 'Zend/Pdf/Action/Named.php'; |
|
184 return new Zend_Pdf_Action_Named($dictionary, $processedActions); |
|
185 break; |
|
186 |
|
187 case 'SubmitForm': |
|
188 require_once 'Zend/Pdf/Action/SubmitForm.php'; |
|
189 return new Zend_Pdf_Action_SubmitForm($dictionary, $processedActions); |
|
190 break; |
|
191 |
|
192 case 'ResetForm': |
|
193 require_once 'Zend/Pdf/Action/ResetForm.php'; |
|
194 return new Zend_Pdf_Action_ResetForm($dictionary, $processedActions); |
|
195 break; |
|
196 |
|
197 case 'ImportData': |
|
198 require_once 'Zend/Pdf/Action/ImportData.php'; |
|
199 return new Zend_Pdf_Action_ImportData($dictionary, $processedActions); |
|
200 break; |
|
201 |
|
202 case 'JavaScript': |
|
203 require_once 'Zend/Pdf/Action/JavaScript.php'; |
|
204 return new Zend_Pdf_Action_JavaScript($dictionary, $processedActions); |
|
205 break; |
|
206 |
|
207 case 'SetOCGState': |
|
208 require_once 'Zend/Pdf/Action/SetOCGState.php'; |
|
209 return new Zend_Pdf_Action_SetOCGState($dictionary, $processedActions); |
|
210 break; |
|
211 |
|
212 case 'Rendition': |
|
213 require_once 'Zend/Pdf/Action/Rendition.php'; |
|
214 return new Zend_Pdf_Action_Rendition($dictionary, $processedActions); |
|
215 break; |
|
216 |
|
217 case 'Trans': |
|
218 require_once 'Zend/Pdf/Action/Trans.php'; |
|
219 return new Zend_Pdf_Action_Trans($dictionary, $processedActions); |
|
220 break; |
|
221 |
|
222 case 'GoTo3DView': |
|
223 require_once 'Zend/Pdf/Action/GoTo3DView.php'; |
|
224 return new Zend_Pdf_Action_GoTo3DView($dictionary, $processedActions); |
|
225 break; |
|
226 |
|
227 default: |
|
228 require_once 'Zend/Pdf/Action/Unknown.php'; |
|
229 return new Zend_Pdf_Action_Unknown($dictionary, $processedActions); |
|
230 break; |
|
231 } |
|
232 } |
|
233 |
|
234 /** |
|
235 * Get resource |
|
236 * |
|
237 * @internal |
|
238 * @return Zend_Pdf_Element |
|
239 */ |
|
240 public function getResource() |
|
241 { |
|
242 return $this->_actionDictionary; |
|
243 } |
|
244 |
|
245 /** |
|
246 * Dump Action and its child actions into PDF structures |
|
247 * |
|
248 * Returns dictionary indirect object or reference |
|
249 * |
|
250 * @internal |
|
251 * @param Zend_Pdf_ElementFactory $factory Object factory for newly created indirect objects |
|
252 * @param SplObjectStorage $processedActions list of already processed actions (used to prevent infinity loop caused by cyclic references) |
|
253 * @return Zend_Pdf_Element_Object|Zend_Pdf_Element_Reference Dictionary indirect object |
|
254 */ |
|
255 public function dumpAction(Zend_Pdf_ElementFactory_Interface $factory, SplObjectStorage $processedActions = null) |
|
256 { |
|
257 if ($processedActions === null) { |
|
258 $processedActions = new SplObjectStorage(); |
|
259 } |
|
260 if ($processedActions->contains($this)) { |
|
261 require_once 'Zend/Pdf/Exception.php'; |
|
262 throw new Zend_Pdf_Exception('Action chain cyclyc reference is detected.'); |
|
263 } |
|
264 $processedActions->attach($this); |
|
265 |
|
266 $childListUpdated = false; |
|
267 if (count($this->_originalNextList) != count($this->next)) { |
|
268 // If original and current children arrays have different size then children list was updated |
|
269 $childListUpdated = true; |
|
270 } else if ( !(array_keys($this->_originalNextList) === array_keys($this->next)) ) { |
|
271 // If original and current children arrays have different keys (with a glance to an order) then children list was updated |
|
272 $childListUpdated = true; |
|
273 } else { |
|
274 foreach ($this->next as $key => $childAction) { |
|
275 if ($this->_originalNextList[$key] !== $childAction) { |
|
276 $childListUpdated = true; |
|
277 break; |
|
278 } |
|
279 } |
|
280 } |
|
281 |
|
282 if ($childListUpdated) { |
|
283 $this->_actionDictionary->touch(); |
|
284 switch (count($this->next)) { |
|
285 case 0: |
|
286 $this->_actionDictionary->Next = null; |
|
287 break; |
|
288 |
|
289 case 1: |
|
290 $child = reset($this->next); |
|
291 $this->_actionDictionary->Next = $child->dumpAction($factory, $processedActions); |
|
292 break; |
|
293 |
|
294 default: |
|
295 require_once 'Zend/Pdf/Element/Array.php'; |
|
296 $pdfChildArray = new Zend_Pdf_Element_Array(); |
|
297 foreach ($this->next as $child) { |
|
298 |
|
299 $pdfChildArray->items[] = $child->dumpAction($factory, $processedActions); |
|
300 } |
|
301 $this->_actionDictionary->Next = $pdfChildArray; |
|
302 break; |
|
303 } |
|
304 } else { |
|
305 foreach ($this->next as $child) { |
|
306 $child->dumpAction($factory, $processedActions); |
|
307 } |
|
308 } |
|
309 |
|
310 if ($this->_actionDictionary instanceof Zend_Pdf_Element_Dictionary) { |
|
311 // It's a newly created action. Register it within object factory and return indirect object |
|
312 return $factory->newObject($this->_actionDictionary); |
|
313 } else { |
|
314 // It's a loaded object |
|
315 return $this->_actionDictionary; |
|
316 } |
|
317 } |
|
318 |
|
319 |
|
320 //////////////////////////////////////////////////////////////////////// |
|
321 // RecursiveIterator interface methods |
|
322 ////////////// |
|
323 |
|
324 /** |
|
325 * Returns current child action. |
|
326 * |
|
327 * @return Zend_Pdf_Action |
|
328 */ |
|
329 public function current() |
|
330 { |
|
331 return current($this->next); |
|
332 } |
|
333 |
|
334 /** |
|
335 * Returns current iterator key |
|
336 * |
|
337 * @return integer |
|
338 */ |
|
339 public function key() |
|
340 { |
|
341 return key($this->next); |
|
342 } |
|
343 |
|
344 /** |
|
345 * Go to next child |
|
346 */ |
|
347 public function next() |
|
348 { |
|
349 return next($this->next); |
|
350 } |
|
351 |
|
352 /** |
|
353 * Rewind children |
|
354 */ |
|
355 public function rewind() |
|
356 { |
|
357 return reset($this->next); |
|
358 } |
|
359 |
|
360 /** |
|
361 * Check if current position is valid |
|
362 * |
|
363 * @return boolean |
|
364 */ |
|
365 public function valid() |
|
366 { |
|
367 return current($this->next) !== false; |
|
368 } |
|
369 |
|
370 /** |
|
371 * Returns the child action. |
|
372 * |
|
373 * @return Zend_Pdf_Action|null |
|
374 */ |
|
375 public function getChildren() |
|
376 { |
|
377 return current($this->next); |
|
378 } |
|
379 |
|
380 /** |
|
381 * Implements RecursiveIterator interface. |
|
382 * |
|
383 * @return bool whether container has any pages |
|
384 */ |
|
385 public function hasChildren() |
|
386 { |
|
387 return count($this->next) > 0; |
|
388 } |
|
389 |
|
390 |
|
391 //////////////////////////////////////////////////////////////////////// |
|
392 // Countable interface methods |
|
393 ////////////// |
|
394 |
|
395 /** |
|
396 * count() |
|
397 * |
|
398 * @return int |
|
399 */ |
|
400 public function count() |
|
401 { |
|
402 return count($this->childOutlines); |
|
403 } |
|
404 } |