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.

446 lines
12 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. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /** Zend_Pdf_ElementFactory_Interface */
  22. // require_once 'Zend/Pdf/ElementFactory/Interface.php';
  23. /**
  24. * PDF element factory.
  25. * Responsibility is to log PDF changes
  26. *
  27. * @package Zend_Pdf
  28. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. class Zend_Pdf_ElementFactory implements Zend_Pdf_ElementFactory_Interface
  32. {
  33. /**
  34. * List of the modified objects.
  35. * Also contains new and removed objects
  36. *
  37. * Array: ojbectNumber => Zend_Pdf_Element_Object
  38. *
  39. * @var array
  40. */
  41. private $_modifiedObjects = array();
  42. /**
  43. * List of the removed objects
  44. *
  45. * Array: ojbectNumber => Zend_Pdf_Element_Object
  46. *
  47. * @var SplObjectStorage
  48. */
  49. private $_removedObjects;
  50. /**
  51. * List of registered objects.
  52. * Used for resources clean up when factory is destroyed.
  53. *
  54. * Array of Zend_Pdf_Element objects
  55. *
  56. * @var array
  57. */
  58. private $_registeredObjects = array();
  59. /**
  60. * PDF object counter.
  61. * Actually it's an object number for new PDF object
  62. *
  63. * @var integer
  64. */
  65. private $_objectCount;
  66. /**
  67. * List of the attached object factories.
  68. * Array of Zend_Pdf_ElementFactory_Interface objects
  69. *
  70. * @var array
  71. */
  72. private $_attachedFactories = array();
  73. /**
  74. * Factory internal id
  75. *
  76. * @var integer
  77. */
  78. private $_factoryId;
  79. /**
  80. * Identity, used for factory id generation
  81. *
  82. * @var integer
  83. */
  84. private static $_identity = 0;
  85. /**
  86. * Internal cache to save calculated shifts
  87. *
  88. * @var array
  89. */
  90. private $_shiftCalculationCache = array();
  91. /**
  92. * Object constructor
  93. *
  94. * @param integer $objCount
  95. */
  96. public function __construct($objCount)
  97. {
  98. $this->_objectCount = (int)$objCount;
  99. $this->_factoryId = self::$_identity++;
  100. $this->_removedObjects = new SplObjectStorage();
  101. }
  102. /**
  103. * Get factory
  104. *
  105. * @return Zend_Pdf_ElementFactory_Interface
  106. */
  107. public function getFactory()
  108. {
  109. return $this;
  110. }
  111. /**
  112. * Factory generator
  113. *
  114. * @param integer $objCount
  115. * @return Zend_Pdf_ElementFactory_Interface
  116. */
  117. static public function createFactory($objCount)
  118. {
  119. // require_once 'Zend/Pdf/ElementFactory/Proxy.php';
  120. return new Zend_Pdf_ElementFactory_Proxy(new Zend_Pdf_ElementFactory($objCount));
  121. }
  122. /**
  123. * Close factory and clean-up resources
  124. *
  125. * @internal
  126. */
  127. public function close()
  128. {
  129. $this->_modifiedObjects = null;
  130. $this->_removedObjects = null;
  131. $this->_attachedFactories = null;
  132. foreach ($this->_registeredObjects as $obj) {
  133. $obj->cleanUp();
  134. }
  135. $this->_registeredObjects = null;
  136. }
  137. /**
  138. * Get source factory object
  139. *
  140. * @return Zend_Pdf_ElementFactory
  141. */
  142. public function resolve()
  143. {
  144. return $this;
  145. }
  146. /**
  147. * Get factory ID
  148. *
  149. * @return integer
  150. */
  151. public function getId()
  152. {
  153. return $this->_factoryId;
  154. }
  155. /**
  156. * Set object counter
  157. *
  158. * @param integer $objCount
  159. */
  160. public function setObjectCount($objCount)
  161. {
  162. $this->_objectCount = (int)$objCount;
  163. }
  164. /**
  165. * Get object counter
  166. *
  167. * @return integer
  168. */
  169. public function getObjectCount()
  170. {
  171. $count = $this->_objectCount;
  172. foreach ($this->_attachedFactories as $attached) {
  173. $count += $attached->getObjectCount() - 1; // -1 as "0" object is a special case and shared between factories
  174. }
  175. return $count;
  176. }
  177. /**
  178. * Attach factory to the current;
  179. *
  180. * @param Zend_Pdf_ElementFactory_Interface $factory
  181. */
  182. public function attach(Zend_Pdf_ElementFactory_Interface $factory)
  183. {
  184. if ( $factory === $this || isset($this->_attachedFactories[$factory->getId()])) {
  185. /**
  186. * Don't attach factory twice.
  187. * We do not check recusively because of nature of attach operation
  188. * (Pages are always attached to the Documents, Fonts are always attached
  189. * to the pages even if pages already use Document level object factory and so on)
  190. */
  191. return;
  192. }
  193. $this->_attachedFactories[$factory->getId()] = $factory;
  194. }
  195. /**
  196. * Calculate object enumeration shift.
  197. *
  198. * @param Zend_Pdf_ElementFactory_Interface $factory
  199. * @return integer
  200. */
  201. public function calculateShift(Zend_Pdf_ElementFactory_Interface $factory)
  202. {
  203. if ($factory === $this) {
  204. return 0;
  205. }
  206. if (isset($this->_shiftCalculationCache[$factory->_factoryId])) {
  207. return $this->_shiftCalculationCache[$factory->_factoryId];
  208. }
  209. $shift = $this->_objectCount - 1;
  210. foreach ($this->_attachedFactories as $subFactory) {
  211. $subFactoryShift = $subFactory->calculateShift($factory);
  212. if ($subFactoryShift != -1) {
  213. // context found
  214. $this->_shiftCalculationCache[$factory->_factoryId] = $shift + $subFactoryShift;
  215. return $shift + $subFactoryShift;
  216. } else {
  217. $shift += $subFactory->getObjectCount()-1;
  218. }
  219. }
  220. $this->_shiftCalculationCache[$factory->_factoryId] = -1;
  221. return -1;
  222. }
  223. /**
  224. * Clean enumeration shift cache.
  225. * Has to be used after PDF render operation to let followed updates be correct.
  226. */
  227. public function cleanEnumerationShiftCache()
  228. {
  229. $this->_shiftCalculationCache = array();
  230. foreach ($this->_attachedFactories as $attached) {
  231. $attached->cleanEnumerationShiftCache();
  232. }
  233. }
  234. /**
  235. * Retrive object enumeration shift.
  236. *
  237. * @param Zend_Pdf_ElementFactory_Interface $factory
  238. * @return integer
  239. * @throws Zend_Pdf_Exception
  240. */
  241. public function getEnumerationShift(Zend_Pdf_ElementFactory_Interface $factory)
  242. {
  243. if (($shift = $this->calculateShift($factory)) == -1) {
  244. // require_once 'Zend/Pdf/Exception.php';
  245. throw new Zend_Pdf_Exception('Wrong object context');
  246. }
  247. return $shift;
  248. }
  249. /**
  250. * Mark object as modified in context of current factory.
  251. *
  252. * @param Zend_Pdf_Element_Object $obj
  253. * @throws Zend_Pdf_Exception
  254. */
  255. public function markAsModified(Zend_Pdf_Element_Object $obj)
  256. {
  257. if ($obj->getFactory() !== $this) {
  258. // require_once 'Zend/Pdf/Exception.php';
  259. throw new Zend_Pdf_Exception('Object is not generated by this factory');
  260. }
  261. $this->_modifiedObjects[$obj->getObjNum()] = $obj;
  262. }
  263. /**
  264. * Remove object in context of current factory.
  265. *
  266. * @param Zend_Pdf_Element_Object $obj
  267. * @throws Zend_Pdf_Exception
  268. */
  269. public function remove(Zend_Pdf_Element_Object $obj)
  270. {
  271. if (!$obj->compareFactory($this)) {
  272. // require_once 'Zend/Pdf/Exception.php';
  273. throw new Zend_Pdf_Exception('Object is not generated by this factory');
  274. }
  275. $this->_modifiedObjects[$obj->getObjNum()] = $obj;
  276. $this->_removedObjects->attach($obj);
  277. }
  278. /**
  279. * Generate new Zend_Pdf_Element_Object
  280. *
  281. * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
  282. *
  283. * @param Zend_Pdf_Element $objectValue
  284. * @return Zend_Pdf_Element_Object
  285. */
  286. public function newObject(Zend_Pdf_Element $objectValue)
  287. {
  288. // require_once 'Zend/Pdf/Element/Object.php';
  289. $obj = new Zend_Pdf_Element_Object($objectValue, $this->_objectCount++, 0, $this);
  290. $this->_modifiedObjects[$obj->getObjNum()] = $obj;
  291. return $obj;
  292. }
  293. /**
  294. * Generate new Zend_Pdf_Element_Object_Stream
  295. *
  296. * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
  297. *
  298. * @param mixed $objectValue
  299. * @return Zend_Pdf_Element_Object_Stream
  300. */
  301. public function newStreamObject($streamValue)
  302. {
  303. // require_once 'Zend/Pdf/Element/Object/Stream.php';
  304. $obj = new Zend_Pdf_Element_Object_Stream($streamValue, $this->_objectCount++, 0, $this);
  305. $this->_modifiedObjects[$obj->getObjNum()] = $obj;
  306. return $obj;
  307. }
  308. /**
  309. * Enumerate modified objects.
  310. * Returns array of Zend_Pdf_UpdateInfoContainer
  311. *
  312. * @param Zend_Pdf_ElementFactory_Interface $rootFactory
  313. * @return array
  314. */
  315. public function listModifiedObjects($rootFactory = null)
  316. {
  317. if ($rootFactory == null) {
  318. $rootFactory = $this;
  319. $shift = 0;
  320. } else {
  321. $shift = $rootFactory->getEnumerationShift($this);
  322. }
  323. ksort($this->_modifiedObjects);
  324. $result = array();
  325. // require_once 'Zend/Pdf/UpdateInfoContainer.php';
  326. foreach ($this->_modifiedObjects as $objNum => $obj) {
  327. if ($this->_removedObjects->contains($obj)) {
  328. $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
  329. $obj->getGenNum()+1,
  330. true);
  331. } else {
  332. $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
  333. $obj->getGenNum(),
  334. false,
  335. $obj->dump($rootFactory));
  336. }
  337. }
  338. foreach ($this->_attachedFactories as $factory) {
  339. $result += $factory->listModifiedObjects($rootFactory);
  340. }
  341. return $result;
  342. }
  343. /**
  344. * Register object in the factory
  345. *
  346. * It's used to clear "parent object" referencies when factory is closed and clean up resources
  347. *
  348. * @param string $refString
  349. * @param Zend_Pdf_Element_Object $obj
  350. */
  351. public function registerObject(Zend_Pdf_Element_Object $obj, $refString)
  352. {
  353. $this->_registeredObjects[$refString] = $obj;
  354. }
  355. /**
  356. * Fetch object specified by reference
  357. *
  358. * @param string $refString
  359. * @return Zend_Pdf_Element_Object|null
  360. */
  361. public function fetchObject($refString)
  362. {
  363. if (!isset($this->_registeredObjects[$refString])) {
  364. return null;
  365. }
  366. return $this->_registeredObjects[$refString];
  367. }
  368. /**
  369. * Check if PDF file was modified
  370. *
  371. * @return boolean
  372. */
  373. public function isModified()
  374. {
  375. if (count($this->_modifiedObjects) != 0) {
  376. return true;
  377. }
  378. foreach ($this->_attachedFactories as $subFactory) {
  379. if ($subFactory->isModified()) {
  380. return true;
  381. }
  382. }
  383. return false;
  384. }
  385. }