PageRenderTime 675ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Pdf.php

https://bitbucket.org/baruffaldi/cms-php-bfcms
PHP | 900 lines | 412 code | 140 blank | 348 comment | 77 complexity | ca8b7a57ea4e4b601fda5136005fa57a MD5 | raw file
  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-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /** Zend_Pdf_Exception */
  21. require_once 'Zend/Pdf/Exception.php';
  22. /** Zend_Pdf_Page */
  23. require_once 'Zend/Pdf/Page.php';
  24. /** Zend_Pdf_Cmap */
  25. require_once 'Zend/Pdf/Cmap.php';
  26. /** Zend_Pdf_Font */
  27. require_once 'Zend/Pdf/Font.php';
  28. /** Zend_Pdf_Style */
  29. require_once 'Zend/Pdf/Style.php';
  30. /** Zend_Pdf_Parser */
  31. require_once 'Zend/Pdf/Parser.php';
  32. /** Zend_Pdf_Trailer */
  33. require_once 'Zend/Pdf/Trailer.php';
  34. /** Zend_Pdf_Trailer_Generator */
  35. require_once 'Zend/Pdf/Trailer/Generator.php';
  36. /** Zend_Pdf_Color */
  37. require_once 'Zend/Pdf/Color.php';
  38. /** Zend_Pdf_Color_GrayScale */
  39. require_once 'Zend/Pdf/Color/GrayScale.php';
  40. /** Zend_Pdf_Color_Rgb */
  41. require_once 'Zend/Pdf/Color/Rgb.php';
  42. /** Zend_Pdf_Color_Cmyk */
  43. require_once 'Zend/Pdf/Color/Cmyk.php';
  44. /** Zend_Pdf_Color_Html */
  45. require_once 'Zend/Pdf/Color/Html.php';
  46. /** Zend_Pdf_Image */
  47. require_once 'Zend/Pdf/Resource/Image.php';
  48. /** Zend_Pdf_Image */
  49. require_once 'Zend/Pdf/Image.php';
  50. /** Zend_Pdf_Image_Jpeg */
  51. require_once 'Zend/Pdf/Resource/Image/Jpeg.php';
  52. /** Zend_Pdf_Image_Tiff */
  53. require_once 'Zend/Pdf/Resource/Image/Tiff.php';
  54. /** Zend_Pdf_Image_Png */
  55. require_once 'Zend/Pdf/Resource/Image/Png.php';
  56. /** Zend_Memory */
  57. require_once 'Zend/Memory.php';
  58. /**
  59. * General entity which describes PDF document.
  60. * It implements document abstraction with a document level operations.
  61. *
  62. * Class is used to create new PDF document or load existing document.
  63. * See details in a class constructor description
  64. *
  65. * Class agregates document level properties and entities (pages, bookmarks,
  66. * document level actions, attachments, form object, etc)
  67. *
  68. * @category Zend
  69. * @package Zend_Pdf
  70. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  71. * @license http://framework.zend.com/license/new-bsd New BSD License
  72. */
  73. class Zend_Pdf
  74. {
  75. /**** Class Constants ****/
  76. /**
  77. * Version number of generated PDF documents.
  78. */
  79. const PDF_VERSION = 1.4;
  80. /**
  81. * PDF file header.
  82. */
  83. const PDF_HEADER = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
  84. /**
  85. * Pages collection
  86. *
  87. * @todo implement it as a class, which supports ArrayAccess and Iterator interfaces,
  88. * to provide incremental parsing and pages tree updating.
  89. * That will give good performance and memory (PDF size) benefits.
  90. *
  91. * @var array - array of Zend_Pdf_Page object
  92. */
  93. public $pages = array();
  94. /**
  95. * Document properties
  96. *
  97. * It's an associative array with PDF meta information, values may
  98. * be string, boolean or float.
  99. * Returned array could be used directly to access, add, modify or remove
  100. * document properties.
  101. *
  102. * Standard document properties: Title (must be set for PDF/X documents), Author,
  103. * Subject, Keywords (comma separated list), Creator (the name of the application,
  104. * that created document, if it was converted from other format), Trapped (must be
  105. * true, false or null, can not be null for PDF/X documents)
  106. *
  107. * @var array
  108. */
  109. public $properties = array();
  110. /**
  111. * Original properties set.
  112. *
  113. * Used for tracking properties changes
  114. *
  115. * @var array
  116. */
  117. private $_originalProperties = array();
  118. /**
  119. * Document level javascript
  120. *
  121. * @var string
  122. */
  123. private $_javaScript = null;
  124. /**
  125. * Document named actions
  126. * "GoTo..." actions, used to refer document parts
  127. * from outside PDF
  128. *
  129. * @var array - array of Zend_Pdf_Action objects
  130. */
  131. private $_namedActions = array();
  132. /**
  133. * Pdf trailer (last or just created)
  134. *
  135. * @var Zend_Pdf_Trailer
  136. */
  137. private $_trailer = null;
  138. /**
  139. * PDF objects factory.
  140. *
  141. * @var Zend_Pdf_ElementFactory_Interface
  142. */
  143. private $_objFactory = null;
  144. /**
  145. * Memory manager for stream objects
  146. *
  147. * @var Zend_Memory_Manager|null
  148. */
  149. private static $_memoryManager = null;
  150. /**
  151. * Pdf file parser.
  152. * It's not used, but has to be destroyed only with Zend_Pdf object
  153. *
  154. * @var Zend_Pdf_Parser
  155. */
  156. private $_parser;
  157. /**
  158. * Request used memory manager
  159. *
  160. * @return Zend_Memory_Manager
  161. */
  162. static public function getMemoryManager()
  163. {
  164. if (self::$_memoryManager === null) {
  165. self::$_memoryManager = Zend_Memory::factory('none');
  166. }
  167. return self::$_memoryManager;
  168. }
  169. /**
  170. * Set user defined memory manager
  171. *
  172. * @param Zend_Memory_Manager $memoryManager
  173. */
  174. static public function setMemoryManager(Zend_Memory_Manager $memoryManager)
  175. {
  176. self::$_memoryManager = $memoryManager;
  177. }
  178. /**
  179. * Create new PDF document from a $source string
  180. *
  181. * @param string $source
  182. * @param integer $revision
  183. * @return Zend_Pdf
  184. */
  185. public static function parse(&$source = null, $revision = null)
  186. {
  187. return new Zend_Pdf($source, $revision);
  188. }
  189. /**
  190. * Load PDF document from a file
  191. *
  192. * @param string $source
  193. * @param integer $revision
  194. * @return Zend_Pdf
  195. */
  196. public static function load($source = null, $revision = null)
  197. {
  198. return new Zend_Pdf($source, $revision, true);
  199. }
  200. /**
  201. * Render PDF document and save it.
  202. *
  203. * If $updateOnly is true, then it only appends new section to the end of file.
  204. *
  205. * @param string $filename
  206. * @param boolean $updateOnly
  207. * @throws Zend_Pdf_Exception
  208. */
  209. public function save($filename, $updateOnly = false)
  210. {
  211. if (($file = @fopen($filename, $updateOnly ? 'ab':'wb')) === false ) {
  212. throw new Zend_Pdf_Exception( "Can not open '$filename' file for writing." );
  213. }
  214. $this->render($updateOnly, $file);
  215. fclose($file);
  216. }
  217. /**
  218. * Creates or loads PDF document.
  219. *
  220. * If $source is null, then it creates a new document.
  221. *
  222. * If $source is a string and $load is false, then it loads document
  223. * from a binary string.
  224. *
  225. * If $source is a string and $load is true, then it loads document
  226. * from a file.
  227. * $revision used to roll back document to specified version
  228. * (0 - currtent version, 1 - previous version, 2 - ...)
  229. *
  230. * @param string $source - PDF file to load
  231. * @param integer $revision
  232. * @throws Zend_Pdf_Exception
  233. * @return Zend_Pdf
  234. */
  235. public function __construct($source = null, $revision = null, $load = false)
  236. {
  237. $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
  238. if ($source !== null) {
  239. $this->_parser = new Zend_Pdf_Parser($source, $this->_objFactory, $load);
  240. $this->_trailer = $this->_parser->getTrailer();
  241. if ($revision !== null) {
  242. $this->rollback($revision);
  243. } else {
  244. $this->_loadPages($this->_trailer->Root->Pages);
  245. }
  246. if ($this->_trailer->Info !== null) {
  247. foreach ($this->_trailer->Info->getKeys() as $key) {
  248. $this->properties[$key] = $this->_trailer->Info->$key->value;
  249. }
  250. if (isset($this->properties['Trapped'])) {
  251. switch ($this->properties['Trapped']) {
  252. case 'True':
  253. $this->properties['Trapped'] = true;
  254. break;
  255. case 'False':
  256. $this->properties['Trapped'] = false;
  257. break;
  258. case 'Unknown':
  259. $this->properties['Trapped'] = null;
  260. break;
  261. default:
  262. // Wrong property value
  263. // Do nothing
  264. break;
  265. }
  266. }
  267. $this->_originalProperties = $this->properties;
  268. }
  269. } else {
  270. $trailerDictionary = new Zend_Pdf_Element_Dictionary();
  271. /**
  272. * Document id
  273. */
  274. $docId = md5(uniqid(rand(), true)); // 32 byte (128 bit) identifier
  275. $docIdLow = substr($docId, 0, 16); // first 16 bytes
  276. $docIdHigh = substr($docId, 16, 16); // second 16 bytes
  277. $trailerDictionary->ID = new Zend_Pdf_Element_Array();
  278. $trailerDictionary->ID->items[] = new Zend_Pdf_Element_String_Binary($docIdLow);
  279. $trailerDictionary->ID->items[] = new Zend_Pdf_Element_String_Binary($docIdHigh);
  280. $trailerDictionary->Size = new Zend_Pdf_Element_Numeric(0);
  281. $this->_trailer = new Zend_Pdf_Trailer_Generator($trailerDictionary);
  282. /**
  283. * Document catalog indirect object.
  284. */
  285. $docCatalog = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  286. $docCatalog->Type = new Zend_Pdf_Element_Name('Catalog');
  287. $docCatalog->Version = new Zend_Pdf_Element_Name(Zend_Pdf::PDF_VERSION);
  288. $this->_trailer->Root = $docCatalog;
  289. /**
  290. * Pages container
  291. */
  292. $docPages = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  293. $docPages->Type = new Zend_Pdf_Element_Name('Pages');
  294. $docPages->Kids = new Zend_Pdf_Element_Array();
  295. $docPages->Count = new Zend_Pdf_Element_Numeric(0);
  296. $docCatalog->Pages = $docPages;
  297. }
  298. }
  299. /**
  300. * Retrive number of revisions.
  301. *
  302. * @return integer
  303. */
  304. public function revisions()
  305. {
  306. $revisions = 1;
  307. $currentTrailer = $this->_trailer;
  308. while ($currentTrailer->getPrev() !== null && $currentTrailer->getPrev()->Root !== null ) {
  309. $revisions++;
  310. $currentTrailer = $currentTrailer->getPrev();
  311. }
  312. return $revisions++;
  313. }
  314. /**
  315. * Rollback document $steps number of revisions.
  316. * This method must be invoked before any changes, applied to the document.
  317. * Otherwise behavior is undefined.
  318. *
  319. * @param integer $steps
  320. */
  321. public function rollback($steps)
  322. {
  323. for ($count = 0; $count < $steps; $count++) {
  324. if ($this->_trailer->getPrev() !== null && $this->_trailer->getPrev()->Root !== null) {
  325. $this->_trailer = $this->_trailer->getPrev();
  326. } else {
  327. break;
  328. }
  329. }
  330. $this->_objFactory->setObjectCount($this->_trailer->Size->value);
  331. // Mark content as modified to force new trailer generation at render time
  332. $this->_trailer->Root->touch();
  333. $this->pages = array();
  334. $this->_loadPages($this->_trailer->Root->Pages);
  335. }
  336. /**
  337. * List of inheritable attributesfor pages tree
  338. *
  339. * @var array
  340. */
  341. private static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
  342. /**
  343. * Load pages recursively
  344. *
  345. * @param Zend_Pdf_Element_Reference $pages
  346. * @param array|null $attributes
  347. */
  348. private function _loadPages(Zend_Pdf_Element_Reference $pages, $attributes = array())
  349. {
  350. if ($pages->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
  351. throw new Zend_Pdf_Exception('Wrong argument');
  352. }
  353. foreach ($pages->getKeys() as $property) {
  354. if (in_array($property, self::$_inheritableAttributes)) {
  355. $attributes[$property] = $pages->$property;
  356. $pages->$property = null;
  357. }
  358. }
  359. foreach ($pages->Kids->items as $child) {
  360. if ($child->Type->value == 'Pages') {
  361. $this->_loadPages($child, $attributes);
  362. } else if ($child->Type->value == 'Page') {
  363. foreach (self::$_inheritableAttributes as $property) {
  364. if ($child->$property === null && array_key_exists($property, $attributes)) {
  365. /**
  366. * Important note.
  367. * If any attribute or dependant object is an indirect object, then it's still
  368. * shared between pages.
  369. */
  370. if ($attributes[$property] instanceof Zend_Pdf_Element_Object) {
  371. $child->$property = $attributes[$property];
  372. } else {
  373. $child->$property = $this->_objFactory->newObject($attributes[$property]);
  374. }
  375. }
  376. }
  377. $this->pages[] = new Zend_Pdf_Page($child, $this->_objFactory);
  378. }
  379. }
  380. }
  381. /**
  382. * Orginize pages to tha pages tree structure.
  383. *
  384. * @todo atomatically attach page to the document, if it's not done yet.
  385. * @todo check, that page is attached to the current document
  386. *
  387. * @todo Dump pages as a balanced tree instead of a plain set.
  388. */
  389. private function _dumpPages()
  390. {
  391. $pagesContainer = $this->_trailer->Root->Pages;
  392. $pagesContainer->touch();
  393. $pagesContainer->Kids->items->clear();
  394. foreach ($this->pages as $page ) {
  395. $page->render($this->_objFactory);
  396. $pageDictionary = $page->getPageDictionary();
  397. $pageDictionary->touch();
  398. $pageDictionary->Parent = $pagesContainer;
  399. $pagesContainer->Kids->items[] = $pageDictionary;
  400. }
  401. $pagesContainer->Count->touch();
  402. $pagesContainer->Count->value = count($this->pages);
  403. }
  404. /**
  405. * Create page object, attached to the PDF document.
  406. * Method signatures:
  407. *
  408. * 1. Create new page with a specified pagesize.
  409. * If $factory is null then it will be created and page must be attached to the document to be
  410. * included into output.
  411. * ---------------------------------------------------------
  412. * new Zend_Pdf_Page(string $pagesize);
  413. * ---------------------------------------------------------
  414. *
  415. * 2. Create new page with a specified pagesize (in default user space units).
  416. * If $factory is null then it will be created and page must be attached to the document to be
  417. * included into output.
  418. * ---------------------------------------------------------
  419. * new Zend_Pdf_Page(numeric $width, numeric $height);
  420. * ---------------------------------------------------------
  421. *
  422. * @param mixed $param1
  423. * @param mixed $param2
  424. * @return Zend_Pdf_Page
  425. */
  426. public function newPage($param1, $param2 = null)
  427. {
  428. if ($param2 === null) {
  429. return new Zend_Pdf_Page($param1, $this->_objFactory);
  430. } else {
  431. return new Zend_Pdf_Page($param1, $param2, $this->_objFactory);
  432. }
  433. }
  434. /**
  435. * Return the document-level Metadata
  436. * or null Metadata stream is not presented
  437. *
  438. * @return string
  439. */
  440. public function getMetadata()
  441. {
  442. if ($this->_trailer->Root->Metadata !== null) {
  443. return $this->_trailer->Root->Metadata->value;
  444. } else {
  445. return null;
  446. }
  447. }
  448. /**
  449. * Sets the document-level Metadata (mast be valid XMP document)
  450. *
  451. * @param string $metadata
  452. */
  453. public function setMetadata($metadata)
  454. {
  455. $metadataObject = $this->_objFactory->newStreamObject($metadata);
  456. $metadataObject->dictionary->Type = new Zend_Pdf_Element_Name('Metadata');
  457. $metadataObject->dictionary->Subtype = new Zend_Pdf_Element_Name('XML');
  458. $this->_trailer->Root->Metadata = $metadataObject;
  459. $this->_trailer->Root->touch();
  460. }
  461. /**
  462. * Return the document-level JavaScript
  463. * or null if there is no JavaScript for this document
  464. *
  465. * @return string
  466. */
  467. public function getJavaScript()
  468. {
  469. return $this->_javaScript;
  470. }
  471. /**
  472. * Return an associative array containing all the named actions in the PDF.
  473. * Named actions (it's always "GoTo" actions) can be used to reference from outside
  474. * the PDF, ex: 'http://www.something.com/mydocument.pdf#MyAction'
  475. *
  476. * @return array
  477. */
  478. public function getNamedActions()
  479. {
  480. return $this->_namedActions;
  481. }
  482. /**
  483. * Extract fonts attached to the document
  484. *
  485. * returns array of Zend_Pdf_Resource_Font_Extracted objects
  486. *
  487. * @return array
  488. */
  489. public function extractFonts()
  490. {
  491. $fontResourcesUnique = array();
  492. foreach ($this->pages as $page) {
  493. $pageResources = $page->extractResources();
  494. if ($pageResources->Font === null) {
  495. // Page doesn't contain have any font reference
  496. continue;
  497. }
  498. $fontResources = $pageResources->Font;
  499. foreach ($fontResources->getKeys() as $fontResourceName) {
  500. $fontDictionary = $fontResources->$fontResourceName;
  501. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  502. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  503. // Font dictionary has to be an indirect object or object reference
  504. continue;
  505. }
  506. $fontResourcesUnique[$fontDictionary->toString($this->_objFactory)] = $fontDictionary;
  507. }
  508. }
  509. $fonts = array();
  510. foreach ($fontResourcesUnique as $resourceReference => $fontDictionary) {
  511. try {
  512. // Try to extract font
  513. $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  514. $fonts[$resourceReference] = $extractedFont;
  515. } catch (Zend_Pdf_Exception $e) {
  516. if ($e->getMessage() != 'Unsupported font type.') {
  517. throw $e;
  518. }
  519. }
  520. }
  521. return $fonts;
  522. }
  523. /**
  524. * Extract font attached to the page by specific font name
  525. *
  526. * $fontName should be specified in UTF-8 encoding
  527. *
  528. * @return Zend_Pdf_Resource_Font_Extracted|null
  529. */
  530. public function extractFont($fontName)
  531. {
  532. $fontResourcesUnique = array();
  533. foreach ($this->pages as $page) {
  534. $pageResources = $page->extractResources();
  535. if ($pageResources->Font === null) {
  536. // Page doesn't contain have any font reference
  537. continue;
  538. }
  539. $fontResources = $pageResources->Font;
  540. foreach ($fontResources->getKeys() as $fontResourceName) {
  541. $fontDictionary = $fontResources->$fontResourceName;
  542. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  543. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  544. // Font dictionary has to be an indirect object or object reference
  545. continue;
  546. }
  547. $resourceReference = $fontDictionary->toString($this->_objFactory);
  548. if (isset($fontResourcesUnique[$resourceReference])) {
  549. continue;
  550. } else {
  551. // Mark resource as processed
  552. $fontResourcesUnique[$resourceReference] = 1;
  553. }
  554. if ($fontDictionary->BaseFont->value != $fontName) {
  555. continue;
  556. }
  557. try {
  558. // Try to extract font
  559. return new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  560. } catch (Zend_Pdf_Exception $e) {
  561. if ($e->getMessage() != 'Unsupported font type.') {
  562. throw $e;
  563. }
  564. // Continue searhing
  565. }
  566. }
  567. }
  568. return null;
  569. }
  570. /**
  571. * Render the completed PDF to a string.
  572. * If $newSegmentOnly is true, then only appended part of PDF is returned.
  573. *
  574. * @param boolean $newSegmentOnly
  575. * @param resource $outputStream
  576. * @return string
  577. * @throws Zend_Pdf_Exception
  578. */
  579. public function render($newSegmentOnly = false, $outputStream = null)
  580. {
  581. // Save document properties if necessary
  582. if ($this->properties != $this->_originalProperties) {
  583. $docInfo = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  584. foreach ($this->properties as $key => $value) {
  585. switch ($key) {
  586. case 'Trapped':
  587. switch ($value) {
  588. case true:
  589. $docInfo->$key = new Zend_Pdf_Element_Name('True');
  590. break;
  591. case false:
  592. $docInfo->$key = new Zend_Pdf_Element_Name('False');
  593. break;
  594. case null:
  595. $docInfo->$key = new Zend_Pdf_Element_Name('Unknown');
  596. break;
  597. default:
  598. throw new Zend_Pdf_Exception('Wrong Trapped document property vale: \'' . $value . '\'. Only true, false and null values are allowed.');
  599. break;
  600. }
  601. case 'CreationDate':
  602. // break intentionally omitted
  603. case 'ModDate':
  604. $docInfo->$key = new Zend_Pdf_Element_String((string)$value);
  605. break;
  606. case 'Title':
  607. // break intentionally omitted
  608. case 'Author':
  609. // break intentionally omitted
  610. case 'Subject':
  611. // break intentionally omitted
  612. case 'Keywords':
  613. // break intentionally omitted
  614. case 'Creator':
  615. // break intentionally omitted
  616. case 'Producer':
  617. // break intentionally omitted
  618. default:
  619. $docInfo->$key = new Zend_Pdf_Element_String((string)$value);
  620. break;
  621. }
  622. }
  623. $this->_trailer->Info = $docInfo;
  624. }
  625. $this->_dumpPages();
  626. // Check, that PDF file was modified
  627. // File is always modified by _dumpPages() now, but future implementations may eliminate this.
  628. if (!$this->_objFactory->isModified()) {
  629. if ($newSegmentOnly) {
  630. // Do nothing, return
  631. return '';
  632. }
  633. if ($outputStream === null) {
  634. return $this->_trailer->getPDFString();
  635. } else {
  636. $pdfData = $this->_trailer->getPDFString();
  637. while ( strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false ) {
  638. $pdfData = substr($pdfData, $byteCount);
  639. }
  640. return '';
  641. }
  642. }
  643. // offset (from a start of PDF file) of new PDF file segment
  644. $offset = $this->_trailer->getPDFLength();
  645. // Last Object number in a list of free objects
  646. $lastFreeObject = $this->_trailer->getLastFreeObject();
  647. // Array of cross-reference table subsections
  648. $xrefTable = array();
  649. // Object numbers of first objects in each subsection
  650. $xrefSectionStartNums = array();
  651. // Last cross-reference table subsection
  652. $xrefSection = array();
  653. // Dummy initialization of the first element (specail case - header of linked list of free objects).
  654. $xrefSection[] = 0;
  655. $xrefSectionStartNums[] = 0;
  656. // Object number of last processed PDF object.
  657. // Used to manage cross-reference subsections.
  658. // Initialized by zero (specail case - header of linked list of free objects).
  659. $lastObjNum = 0;
  660. if ($outputStream !== null) {
  661. if (!$newSegmentOnly) {
  662. $pdfData = $this->_trailer->getPDFString();
  663. while ( strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false ) {
  664. $pdfData = substr($pdfData, $byteCount);
  665. }
  666. }
  667. } else {
  668. $pdfSegmentBlocks = ($newSegmentOnly) ? array() : array($this->_trailer->getPDFString());
  669. }
  670. // Iterate objects to create new reference table
  671. foreach ($this->_objFactory->listModifiedObjects() as $updateInfo) {
  672. $objNum = $updateInfo->getObjNum();
  673. if ($objNum - $lastObjNum != 1) {
  674. // Save cross-reference table subsection and start new one
  675. $xrefTable[] = $xrefSection;
  676. $xrefSection = array();
  677. $xrefSectionStartNums[] = $objNum;
  678. }
  679. if ($updateInfo->isFree()) {
  680. // Free object cross-reference table entry
  681. $xrefSection[] = sprintf("%010d %05d f \n", $lastFreeObject, $updateInfo->getGenNum());
  682. $lastFreeObject = $objNum;
  683. } else {
  684. // In-use object cross-reference table entry
  685. $xrefSection[] = sprintf("%010d %05d n \n", $offset, $updateInfo->getGenNum());
  686. $pdfBlock = $updateInfo->getObjectDump();
  687. $offset += strlen($pdfBlock);
  688. if ($outputStream === null) {
  689. $pdfSegmentBlocks[] = $pdfBlock;
  690. } else {
  691. while ( strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false ) {
  692. $pdfBlock = substr($pdfBlock, $byteCount);
  693. }
  694. }
  695. }
  696. $lastObjNum = $objNum;
  697. }
  698. // Save last cross-reference table subsection
  699. $xrefTable[] = $xrefSection;
  700. // Modify first entry (specail case - header of linked list of free objects).
  701. $xrefTable[0][0] = sprintf("%010d 65535 f \n", $lastFreeObject);
  702. $xrefTableStr = "xref\n";
  703. foreach ($xrefTable as $sectId => $xrefSection) {
  704. $xrefTableStr .= sprintf("%d %d \n", $xrefSectionStartNums[$sectId], count($xrefSection));
  705. foreach ($xrefSection as $xrefTableEntry) {
  706. $xrefTableStr .= $xrefTableEntry;
  707. }
  708. }
  709. $this->_trailer->Size->value = $this->_objFactory->getObjectCount();
  710. $pdfBlock = $xrefTableStr
  711. . $this->_trailer->toString()
  712. . "startxref\n" . $offset . "\n"
  713. . "%%EOF\n";
  714. if ($outputStream === null) {
  715. $pdfSegmentBlocks[] = $pdfBlock;
  716. return implode('', $pdfSegmentBlocks);
  717. } else {
  718. while ( strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false ) {
  719. $pdfBlock = substr($pdfBlock, $byteCount);
  720. }
  721. return '';
  722. }
  723. }
  724. /**
  725. * Set the document-level JavaScript
  726. *
  727. * @param string $javascript
  728. */
  729. public function setJavaScript($javascript)
  730. {
  731. $this->_javaScript = $javascript;
  732. }
  733. /**
  734. * Convert date to PDF format (it's close to ASN.1 (Abstract Syntax Notation
  735. * One) defined in ISO/IEC 8824).
  736. *
  737. * @todo This really isn't the best location for this method. It should
  738. * probably actually exist as Zend_Pdf_Element_Date or something like that.
  739. *
  740. * @todo Address the following E_STRICT issue:
  741. * PHP Strict Standards: date(): It is not safe to rely on the system's
  742. * timezone settings. Please use the date.timezone setting, the TZ
  743. * environment variable or the date_default_timezone_set() function. In
  744. * case you used any of those methods and you are still getting this
  745. * warning, you most likely misspelled the timezone identifier.
  746. *
  747. * @param integer $timestamp (optional) If omitted, uses the current time.
  748. * @return string
  749. */
  750. public static function pdfDate($timestamp = null)
  751. {
  752. if (is_null($timestamp)) {
  753. $date = date('\D\:YmdHisO');
  754. } else {
  755. $date = date('\D\:YmdHisO', $timestamp);
  756. }
  757. return substr_replace($date, '\'', -2, 0) . '\'';
  758. }
  759. }