460 lines
16 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  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-2014 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /** Internally used classes */
  23. // require_once 'Zend/Pdf/Element.php';
  24. // require_once 'Zend/Pdf/Element/Array.php';
  25. // require_once 'Zend/Pdf/Element/Numeric.php';
  26. // require_once 'Zend/Pdf/Element/String.php';
  27. /** Zend_Pdf_Outline */
  28. // require_once 'Zend/Pdf/Outline.php';
  29. /**
  30. * Traceable PDF outline representation class
  31. *
  32. * Instances of this class trace object update uperations. That allows to avoid outlines PDF tree update
  33. * which should be performed at each document update otherwise.
  34. *
  35. * @package Zend_Pdf
  36. * @subpackage Outlines
  37. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  38. * @license http://framework.zend.com/license/new-bsd New BSD License
  39. */
  40. class Zend_Pdf_Outline_Loaded extends Zend_Pdf_Outline
  41. {
  42. /**
  43. * Outline dictionary object
  44. *
  45. * @var Zend_Pdf_Element_Dictionary|Zend_Pdf_Element_Object|Zend_Pdf_Element_Reference
  46. */
  47. protected $_outlineDictionary;
  48. /**
  49. * original array of child outlines
  50. *
  51. * @var array
  52. */
  53. protected $_originalChildOutlines = array();
  54. /**
  55. * Get outline title.
  56. *
  57. * @return string
  58. * @throws Zend_Pdf_Exception
  59. */
  60. public function getTitle()
  61. {
  62. if ($this->_outlineDictionary->Title === null) {
  63. // require_once 'Zend/Pdf/Exception.php';
  64. throw new Zend_Pdf_Exception('Outline dictionary Title entry is required.');
  65. }
  66. return $this->_outlineDictionary->Title->value;
  67. }
  68. /**
  69. * Set outline title
  70. *
  71. * @param string $title
  72. * @return Zend_Pdf_Outline
  73. */
  74. public function setTitle($title)
  75. {
  76. $this->_outlineDictionary->Title->touch();
  77. $this->_outlineDictionary->Title = new Zend_Pdf_Element_String($title);
  78. return $this;
  79. }
  80. /**
  81. * Sets 'isOpen' outline flag
  82. *
  83. * @param boolean $isOpen
  84. * @return Zend_Pdf_Outline
  85. */
  86. public function setIsOpen($isOpen)
  87. {
  88. parent::setIsOpen($isOpen);
  89. if ($this->_outlineDictionary->Count === null) {
  90. // Do Nothing.
  91. return this;
  92. }
  93. $childrenCount = $this->_outlineDictionary->Count->value;
  94. $isOpenCurrentState = ($childrenCount > 0);
  95. if ($isOpen != $isOpenCurrentState) {
  96. $this->_outlineDictionary->Count->touch();
  97. $this->_outlineDictionary->Count->value = ($isOpen? 1 : -1)*abs($childrenCount);
  98. }
  99. return $this;
  100. }
  101. /**
  102. * Returns true if outline item is displayed in italic
  103. *
  104. * @return boolean
  105. */
  106. public function isItalic()
  107. {
  108. if ($this->_outlineDictionary->F === null) {
  109. return false;
  110. }
  111. return $this->_outlineDictionary->F->value & 1;
  112. }
  113. /**
  114. * Sets 'isItalic' outline flag
  115. *
  116. * @param boolean $isItalic
  117. * @return Zend_Pdf_Outline
  118. */
  119. public function setIsItalic($isItalic)
  120. {
  121. if ($this->_outlineDictionary->F === null) {
  122. $this->_outlineDictionary->touch();
  123. $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isItalic? 1 : 0);
  124. } else {
  125. $this->_outlineDictionary->F->touch();
  126. if ($isItalic) {
  127. $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 1;
  128. } else {
  129. $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~1;
  130. }
  131. }
  132. return $this;
  133. }
  134. /**
  135. * Returns true if outline item is displayed in bold
  136. *
  137. * @return boolean
  138. */
  139. public function isBold()
  140. {
  141. if ($this->_outlineDictionary->F === null) {
  142. return false;
  143. }
  144. return $this->_outlineDictionary->F->value & 2;
  145. }
  146. /**
  147. * Sets 'isBold' outline flag
  148. *
  149. * @param boolean $isBold
  150. * @return Zend_Pdf_Outline
  151. */
  152. public function setIsBold($isBold)
  153. {
  154. if ($this->_outlineDictionary->F === null) {
  155. $this->_outlineDictionary->touch();
  156. $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isBold? 2 : 0);
  157. } else {
  158. $this->_outlineDictionary->F->touch();
  159. if ($isBold) {
  160. $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 2;
  161. } else {
  162. $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~2;
  163. }
  164. }
  165. return $this;
  166. }
  167. /**
  168. * Get outline text color.
  169. *
  170. * @return Zend_Pdf_Color_Rgb
  171. */
  172. public function getColor()
  173. {
  174. if ($this->_outlineDictionary->C === null) {
  175. return null;
  176. }
  177. $components = $this->_outlineDictionary->C->items;
  178. // require_once 'Zend/Pdf/Color/Rgb.php';
  179. return new Zend_Pdf_Color_Rgb($components[0], $components[1], $components[2]);
  180. }
  181. /**
  182. * Set outline text color.
  183. * (null means default color which is black)
  184. *
  185. * @param Zend_Pdf_Color_Rgb $color
  186. * @return Zend_Pdf_Outline
  187. */
  188. public function setColor(Zend_Pdf_Color_Rgb $color)
  189. {
  190. $this->_outlineDictionary->touch();
  191. if ($color === null) {
  192. $this->_outlineDictionary->C = null;
  193. } else {
  194. $components = $color->getComponents();
  195. $colorComponentElements = array(new Zend_Pdf_Element_Numeric($components[0]),
  196. new Zend_Pdf_Element_Numeric($components[1]),
  197. new Zend_Pdf_Element_Numeric($components[2]));
  198. $this->_outlineDictionary->C = new Zend_Pdf_Element_Array($colorComponentElements);
  199. }
  200. return $this;
  201. }
  202. /**
  203. * Get outline target.
  204. *
  205. * @return Zend_Pdf_Target
  206. * @throws Zend_Pdf_Exception
  207. */
  208. public function getTarget()
  209. {
  210. if ($this->_outlineDictionary->Dest !== null) {
  211. if ($this->_outlineDictionary->A !== null) {
  212. // require_once 'Zend/Pdf/Exception.php';
  213. throw new Zend_Pdf_Exception('Outline dictionary may contain Dest or A entry, but not both.');
  214. }
  215. // require_once 'Zend/Pdf/Destination.php';
  216. return Zend_Pdf_Destination::load($this->_outlineDictionary->Dest);
  217. } else if ($this->_outlineDictionary->A !== null) {
  218. // require_once 'Zend/Pdf/Action.php';
  219. return Zend_Pdf_Action::load($this->_outlineDictionary->A);
  220. }
  221. return null;
  222. }
  223. /**
  224. * Set outline target.
  225. * Null means no target
  226. *
  227. * @param Zend_Pdf_Target|string $target
  228. * @return Zend_Pdf_Outline
  229. * @throws Zend_Pdf_Exception
  230. */
  231. public function setTarget($target = null)
  232. {
  233. $this->_outlineDictionary->touch();
  234. if (is_string($target)) {
  235. // require_once 'Zend/Pdf/Destination/Named.php';
  236. $target = Zend_Pdf_Destination_Named::create($target);
  237. }
  238. if ($target === null) {
  239. $this->_outlineDictionary->Dest = null;
  240. $this->_outlineDictionary->A = null;
  241. } else if ($target instanceof Zend_Pdf_Destination) {
  242. $this->_outlineDictionary->Dest = $target->getResource();
  243. $this->_outlineDictionary->A = null;
  244. } else if ($target instanceof Zend_Pdf_Action) {
  245. $this->_outlineDictionary->Dest = null;
  246. $this->_outlineDictionary->A = $target->getResource();
  247. } else {
  248. // require_once 'Zend/Pdf/Exception.php';
  249. throw new Zend_Pdf_Exception('Outline target has to be Zend_Pdf_Destination or Zend_Pdf_Action object or string');
  250. }
  251. return $this;
  252. }
  253. /**
  254. * Set outline options
  255. *
  256. * @param array $options
  257. * @return Zend_Pdf_Actions_Traceable
  258. * @throws Zend_Pdf_Exception
  259. */
  260. public function setOptions(array $options)
  261. {
  262. parent::setOptions($options);
  263. return $this;
  264. }
  265. /**
  266. * Create PDF outline object using specified dictionary
  267. *
  268. * @internal
  269. * @param Zend_Pdf_Element $dictionary (It's actually Dictionary or Dictionary Object or Reference to a Dictionary Object)
  270. * @param Zend_Pdf_Action $parentAction
  271. * @param SplObjectStorage $processedOutlines List of already processed Outline dictionaries,
  272. * used to avoid cyclic references
  273. * @return Zend_Pdf_Action
  274. * @throws Zend_Pdf_Exception
  275. */
  276. public function __construct(Zend_Pdf_Element $dictionary, SplObjectStorage $processedDictionaries = null)
  277. {
  278. if ($dictionary->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
  279. // require_once 'Zend/Pdf/Exception.php';
  280. throw new Zend_Pdf_Exception('$dictionary mast be an indirect dictionary object.');
  281. }
  282. if ($processedDictionaries === null) {
  283. $processedDictionaries = new SplObjectStorage();
  284. }
  285. $processedDictionaries->attach($dictionary);
  286. $this->_outlineDictionary = $dictionary;
  287. if ($dictionary->Count !== null) {
  288. if ($dictionary->Count->getType() != Zend_Pdf_Element::TYPE_NUMERIC) {
  289. // require_once 'Zend/Pdf/Exception.php';
  290. throw new Zend_Pdf_Exception('Outline dictionary Count entry must be a numeric element.');
  291. }
  292. $childOutlinesCount = $dictionary->Count->value;
  293. if ($childOutlinesCount > 0) {
  294. $this->_open = true;
  295. }
  296. $childOutlinesCount = abs($childOutlinesCount);
  297. $childDictionary = $dictionary->First;
  298. $children = new SplObjectStorage();
  299. while ($childDictionary !== null) {
  300. // Check children structure for cyclic references
  301. if ($children->contains($childDictionary)) {
  302. // require_once 'Zend/Pdf/Exception.php';
  303. throw new Zend_Pdf_Exception('Outline childs load error.');
  304. }
  305. if (!$processedDictionaries->contains($childDictionary)) {
  306. $this->childOutlines[] = new Zend_Pdf_Outline_Loaded($childDictionary, $processedDictionaries);
  307. }
  308. $childDictionary = $childDictionary->Next;
  309. }
  310. $this->_originalChildOutlines = $this->childOutlines;
  311. }
  312. }
  313. /**
  314. * Dump Outline and its child outlines into PDF structures
  315. *
  316. * Returns dictionary indirect object or reference
  317. *
  318. * @internal
  319. * @param Zend_Pdf_ElementFactory $factory object factory for newly created indirect objects
  320. * @param boolean $updateNavigation Update navigation flag
  321. * @param Zend_Pdf_Element $parent Parent outline dictionary reference
  322. * @param Zend_Pdf_Element $prev Previous outline dictionary reference
  323. * @param SplObjectStorage $processedOutlines List of already processed outlines
  324. * @return Zend_Pdf_Element
  325. * @throws Zend_Pdf_Exception
  326. */
  327. public function dumpOutline(Zend_Pdf_ElementFactory_Interface $factory,
  328. $updateNavigation,
  329. Zend_Pdf_Element $parent,
  330. Zend_Pdf_Element $prev = null,
  331. SplObjectStorage $processedOutlines = null)
  332. {
  333. if ($processedOutlines === null) {
  334. $processedOutlines = new SplObjectStorage();
  335. }
  336. $processedOutlines->attach($this);
  337. if ($updateNavigation) {
  338. $this->_outlineDictionary->touch();
  339. $this->_outlineDictionary->Parent = $parent;
  340. $this->_outlineDictionary->Prev = $prev;
  341. $this->_outlineDictionary->Next = null;
  342. }
  343. $updateChildNavigation = false;
  344. if (count($this->_originalChildOutlines) != count($this->childOutlines)) {
  345. // If original and current children arrays have different size then children list was updated
  346. $updateChildNavigation = true;
  347. } else if ( !(array_keys($this->_originalChildOutlines) === array_keys($this->childOutlines)) ) {
  348. // If original and current children arrays have different keys (with a glance to an order) then children list was updated
  349. $updateChildNavigation = true;
  350. } else {
  351. foreach ($this->childOutlines as $key => $childOutline) {
  352. if ($this->_originalChildOutlines[$key] !== $childOutline) {
  353. $updateChildNavigation = true;
  354. break;
  355. }
  356. }
  357. }
  358. $lastChild = null;
  359. if ($updateChildNavigation) {
  360. $this->_outlineDictionary->touch();
  361. $this->_outlineDictionary->First = null;
  362. foreach ($this->childOutlines as $childOutline) {
  363. if ($processedOutlines->contains($childOutline)) {
  364. // require_once 'Zend/Pdf/Exception.php';
  365. throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.');
  366. }
  367. if ($lastChild === null) {
  368. // First pass. Update Outlines dictionary First entry using corresponding value
  369. $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, null, $processedOutlines);
  370. $this->_outlineDictionary->First = $lastChild;
  371. } else {
  372. // Update previous outline dictionary Next entry (Prev is updated within dumpOutline() method)
  373. $childOutlineDictionary = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines);
  374. $lastChild->Next = $childOutlineDictionary;
  375. $lastChild = $childOutlineDictionary;
  376. }
  377. }
  378. $this->_outlineDictionary->Last = $lastChild;
  379. if (count($this->childOutlines) != 0) {
  380. $this->_outlineDictionary->Count = new Zend_Pdf_Element_Numeric(($this->isOpen()? 1 : -1)*count($this->childOutlines));
  381. } else {
  382. $this->_outlineDictionary->Count = null;
  383. }
  384. } else {
  385. foreach ($this->childOutlines as $childOutline) {
  386. if ($processedOutlines->contains($childOutline)) {
  387. // require_once 'Zend/Pdf/Exception.php';
  388. throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.');
  389. }
  390. $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines);
  391. }
  392. }
  393. return $this->_outlineDictionary;
  394. }
  395. public function dump($level = 0)
  396. {
  397. printf(":%3d:%s:%s:%s%s :\n", count($this->childOutlines),$this->isItalic()? 'i':' ', $this->isBold()? 'b':' ', str_pad('', 4*$level), $this->getTitle());
  398. if ($this->isOpen() || true) {
  399. foreach ($this->childOutlines as $child) {
  400. $child->dump($level + 1);
  401. }
  402. }
  403. }
  404. }