You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

460 lines
16 KiB

11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 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-2015 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-2015 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. }