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.

773 lines
27 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. * @copyright Copyright (c) 2005-2015 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. /** Internally used classes */
  22. // require_once 'Zend/Pdf/Element.php';
  23. // require_once 'Zend/Pdf/Element/Array.php';
  24. // require_once 'Zend/Pdf/Element/String/Binary.php';
  25. // require_once 'Zend/Pdf/Element/Boolean.php';
  26. // require_once 'Zend/Pdf/Element/Dictionary.php';
  27. // require_once 'Zend/Pdf/Element/Name.php';
  28. // require_once 'Zend/Pdf/Element/Null.php';
  29. // require_once 'Zend/Pdf/Element/Numeric.php';
  30. // require_once 'Zend/Pdf/Element/String.php';
  31. // require_once 'Zend/Pdf/Resource/Unified.php';
  32. // require_once 'Zend/Pdf/Canvas/Abstract.php';
  33. /**
  34. * PDF Page
  35. *
  36. * @package Zend_Pdf
  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_Page extends Zend_Pdf_Canvas_Abstract
  41. {
  42. /**** Class Constants ****/
  43. /* Page Sizes */
  44. /**
  45. * Size representing an A4 page in portrait (tall) orientation.
  46. */
  47. const SIZE_A4 = '595:842:';
  48. /**
  49. * Size representing an A4 page in landscape (wide) orientation.
  50. */
  51. const SIZE_A4_LANDSCAPE = '842:595:';
  52. /**
  53. * Size representing a US Letter page in portrait (tall) orientation.
  54. */
  55. const SIZE_LETTER = '612:792:';
  56. /**
  57. * Size representing a US Letter page in landscape (wide) orientation.
  58. */
  59. const SIZE_LETTER_LANDSCAPE = '792:612:';
  60. /* Shape Drawing */
  61. /**
  62. * Stroke the path only. Do not fill.
  63. */
  64. const SHAPE_DRAW_STROKE = 0;
  65. /**
  66. * Fill the path only. Do not stroke.
  67. */
  68. const SHAPE_DRAW_FILL = 1;
  69. /**
  70. * Fill and stroke the path.
  71. */
  72. const SHAPE_DRAW_FILL_AND_STROKE = 2;
  73. /* Shape Filling Methods */
  74. /**
  75. * Fill the path using the non-zero winding rule.
  76. */
  77. const FILL_METHOD_NON_ZERO_WINDING = 0;
  78. /**
  79. * Fill the path using the even-odd rule.
  80. */
  81. const FILL_METHOD_EVEN_ODD = 1;
  82. /* Line Dash Types */
  83. /**
  84. * Solid line dash.
  85. */
  86. const LINE_DASHING_SOLID = 0;
  87. /**
  88. * Page dictionary (refers to an inderect Zend_Pdf_Element_Dictionary object).
  89. *
  90. * @var Zend_Pdf_Element_Reference|Zend_Pdf_Element_Object
  91. */
  92. protected $_dictionary;
  93. /**
  94. * PDF objects factory.
  95. *
  96. * @var Zend_Pdf_ElementFactory_Interface
  97. */
  98. protected $_objFactory = null;
  99. /**
  100. * Flag which signals, that page is created separately from any PDF document or
  101. * attached to anyone.
  102. *
  103. * @var boolean
  104. */
  105. protected $_attached;
  106. /**
  107. * Safe Graphics State semafore
  108. *
  109. * If it's false, than we can't be sure Graphics State is restored withing
  110. * context of previous contents stream (ex. drawing coordinate system may be rotated).
  111. * We should encompass existing content with save/restore GS operators
  112. *
  113. * @var boolean
  114. */
  115. protected $_safeGS;
  116. /**
  117. * Object constructor.
  118. * Constructor signatures:
  119. *
  120. * 1. Load PDF page from a parsed PDF file.
  121. * Object factory is created by PDF parser.
  122. * ---------------------------------------------------------
  123. * new Zend_Pdf_Page(Zend_Pdf_Element_Dictionary $pageDict,
  124. * Zend_Pdf_ElementFactory_Interface $factory);
  125. * ---------------------------------------------------------
  126. *
  127. * 2. Make a copy of the PDF page.
  128. * New page is created in the same context as source page. Object factory is shared.
  129. * Thus it will be attached to the document, but need to be placed into Zend_Pdf::$pages array
  130. * to be included into output.
  131. * ---------------------------------------------------------
  132. * new Zend_Pdf_Page(Zend_Pdf_Page $page);
  133. * ---------------------------------------------------------
  134. *
  135. * 3. Create new page with a specified pagesize.
  136. * If $factory is null then it will be created and page must be attached to the document to be
  137. * included into output.
  138. * ---------------------------------------------------------
  139. * new Zend_Pdf_Page(string $pagesize, Zend_Pdf_ElementFactory_Interface $factory = null);
  140. * ---------------------------------------------------------
  141. *
  142. * 4. Create new page with a specified pagesize (in default user space units).
  143. * If $factory is null then it will be created and page must be attached to the document to be
  144. * included into output.
  145. * ---------------------------------------------------------
  146. * new Zend_Pdf_Page(numeric $width, numeric $height, Zend_Pdf_ElementFactory_Interface $factory = null);
  147. * ---------------------------------------------------------
  148. *
  149. *
  150. * @param mixed $param1
  151. * @param mixed $param2
  152. * @param mixed $param3
  153. * @throws Zend_Pdf_Exception
  154. */
  155. public function __construct($param1, $param2 = null, $param3 = null)
  156. {
  157. if (($param1 instanceof Zend_Pdf_Element_Reference ||
  158. $param1 instanceof Zend_Pdf_Element_Object
  159. ) &&
  160. $param2 instanceof Zend_Pdf_ElementFactory_Interface &&
  161. $param3 === null
  162. ) {
  163. switch ($param1->getType()) {
  164. case Zend_Pdf_Element::TYPE_DICTIONARY:
  165. $this->_dictionary = $param1;
  166. $this->_objFactory = $param2;
  167. $this->_attached = true;
  168. $this->_safeGS = false;
  169. return;
  170. break;
  171. case Zend_Pdf_Element::TYPE_NULL:
  172. $this->_objFactory = $param2;
  173. $pageWidth = $pageHeight = 0;
  174. break;
  175. default:
  176. // require_once 'Zend/Pdf/Exception.php';
  177. throw new Zend_Pdf_Exception('Unrecognized object type.');
  178. break;
  179. }
  180. } else if ($param1 instanceof Zend_Pdf_Page && $param2 === null && $param3 === null) {
  181. // Duplicate existing page.
  182. // Let already existing content and resources to be shared between pages
  183. // We don't give existing content modification functionality, so we don't need "deep copy"
  184. $this->_objFactory = $param1->_objFactory;
  185. $this->_attached = &$param1->_attached;
  186. $this->_safeGS = false;
  187. $this->_dictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  188. foreach ($param1->_dictionary->getKeys() as $key) {
  189. if ($key == 'Contents') {
  190. // Clone Contents property
  191. $this->_dictionary->Contents = new Zend_Pdf_Element_Array();
  192. if ($param1->_dictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) {
  193. // Prepare array of content streams and add existing stream
  194. $this->_dictionary->Contents->items[] = $param1->_dictionary->Contents;
  195. } else {
  196. // Clone array of the content streams
  197. foreach ($param1->_dictionary->Contents->items as $srcContentStream) {
  198. $this->_dictionary->Contents->items[] = $srcContentStream;
  199. }
  200. }
  201. } else {
  202. $this->_dictionary->$key = $param1->_dictionary->$key;
  203. }
  204. }
  205. return;
  206. } else if (is_string($param1) &&
  207. ($param2 === null || $param2 instanceof Zend_Pdf_ElementFactory_Interface) &&
  208. $param3 === null) {
  209. if ($param2 !== null) {
  210. $this->_objFactory = $param2;
  211. } else {
  212. // require_once 'Zend/Pdf/ElementFactory.php';
  213. $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
  214. }
  215. $this->_attached = false;
  216. $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */
  217. switch (strtolower($param1)) {
  218. case 'a4':
  219. $param1 = Zend_Pdf_Page::SIZE_A4;
  220. break;
  221. case 'a4-landscape':
  222. $param1 = Zend_Pdf_Page::SIZE_A4_LANDSCAPE;
  223. break;
  224. case 'letter':
  225. $param1 = Zend_Pdf_Page::SIZE_LETTER;
  226. break;
  227. case 'letter-landscape':
  228. $param1 = Zend_Pdf_Page::SIZE_LETTER_LANDSCAPE;
  229. break;
  230. default:
  231. // should be in "x:y" or "x:y:" form
  232. }
  233. $pageDim = explode(':', $param1);
  234. if(count($pageDim) == 2 || count($pageDim) == 3) {
  235. $pageWidth = $pageDim[0];
  236. $pageHeight = $pageDim[1];
  237. } else {
  238. /**
  239. * @todo support of user defined pagesize notations, like:
  240. * "210x297mm", "595x842", "8.5x11in", "612x792"
  241. */
  242. // require_once 'Zend/Pdf/Exception.php';
  243. throw new Zend_Pdf_Exception('Wrong pagesize notation.');
  244. }
  245. /**
  246. * @todo support of pagesize recalculation to "default user space units"
  247. */
  248. } else if (is_numeric($param1) && is_numeric($param2) &&
  249. ($param3 === null || $param3 instanceof Zend_Pdf_ElementFactory_Interface)) {
  250. if ($param3 !== null) {
  251. $this->_objFactory = $param3;
  252. } else {
  253. // require_once 'Zend/Pdf/ElementFactory.php';
  254. $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
  255. }
  256. $this->_attached = false;
  257. $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */
  258. $pageWidth = $param1;
  259. $pageHeight = $param2;
  260. } else {
  261. // require_once 'Zend/Pdf/Exception.php';
  262. throw new Zend_Pdf_Exception('Unrecognized method signature, wrong number of arguments or wrong argument types.');
  263. }
  264. $this->_dictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  265. $this->_dictionary->Type = new Zend_Pdf_Element_Name('Page');
  266. // require_once 'Zend/Pdf.php';
  267. $this->_dictionary->LastModified = new Zend_Pdf_Element_String(Zend_Pdf::pdfDate());
  268. $this->_dictionary->Resources = new Zend_Pdf_Element_Dictionary();
  269. $this->_dictionary->MediaBox = new Zend_Pdf_Element_Array();
  270. $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0);
  271. $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0);
  272. $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageWidth);
  273. $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageHeight);
  274. $this->_dictionary->Contents = new Zend_Pdf_Element_Array();
  275. }
  276. /**
  277. * Attach resource to the canvas
  278. *
  279. * Method returns a name of the resource which can be used
  280. * as a resource reference within drawing instructions stream
  281. * Allowed types: 'ExtGState', 'ColorSpace', 'Pattern', 'Shading',
  282. * 'XObject', 'Font', 'Properties'
  283. *
  284. * @param string $type
  285. * @param Zend_Pdf_Resource $resource
  286. * @return string
  287. */
  288. protected function _attachResource($type, Zend_Pdf_Resource $resource)
  289. {
  290. // Check that Resources dictionary contains appropriate resource set
  291. if ($this->_dictionary->Resources->$type === null) {
  292. $this->_dictionary->Resources->touch();
  293. $this->_dictionary->Resources->$type = new Zend_Pdf_Element_Dictionary();
  294. } else {
  295. $this->_dictionary->Resources->$type->touch();
  296. }
  297. // Check, that resource is already attached to resource set.
  298. $resObject = $resource->getResource();
  299. foreach ($this->_dictionary->Resources->$type->getKeys() as $ResID) {
  300. if ($this->_dictionary->Resources->$type->$ResID === $resObject) {
  301. return $ResID;
  302. }
  303. }
  304. $idCounter = 1;
  305. do {
  306. $newResName = $type[0] . $idCounter++;
  307. } while ($this->_dictionary->Resources->$type->$newResName !== null);
  308. $this->_dictionary->Resources->$type->$newResName = $resObject;
  309. $this->_objFactory->attach($resource->getFactory());
  310. return $newResName;
  311. }
  312. /**
  313. * Add procedureSet to the Page description
  314. *
  315. * @param string $procSetName
  316. */
  317. protected function _addProcSet($procSetName)
  318. {
  319. // Check that Resources dictionary contains ProcSet entry
  320. if ($this->_dictionary->Resources->ProcSet === null) {
  321. $this->_dictionary->Resources->touch();
  322. $this->_dictionary->Resources->ProcSet = new Zend_Pdf_Element_Array();
  323. } else {
  324. $this->_dictionary->Resources->ProcSet->touch();
  325. }
  326. foreach ($this->_dictionary->Resources->ProcSet->items as $procSetEntry) {
  327. if ($procSetEntry->value == $procSetName) {
  328. // Procset is already included into a ProcSet array
  329. return;
  330. }
  331. }
  332. $this->_dictionary->Resources->ProcSet->items[] = new Zend_Pdf_Element_Name($procSetName);
  333. }
  334. /**
  335. * Returns dictionaries of used resources.
  336. *
  337. * Used for canvas implementations interoperability
  338. *
  339. * Structure of the returned array:
  340. * array(
  341. * <resTypeName> => array(
  342. * <resName> => <Zend_Pdf_Resource object>,
  343. * <resName> => <Zend_Pdf_Resource object>,
  344. * <resName> => <Zend_Pdf_Resource object>,
  345. * ...
  346. * ),
  347. * <resTypeName> => array(
  348. * <resName> => <Zend_Pdf_Resource object>,
  349. * <resName> => <Zend_Pdf_Resource object>,
  350. * <resName> => <Zend_Pdf_Resource object>,
  351. * ...
  352. * ),
  353. * ...
  354. * 'ProcSet' => array()
  355. * )
  356. *
  357. * where ProcSet array is a list of used procedure sets names (strings).
  358. * Allowed procedure set names: 'PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'
  359. *
  360. * @internal
  361. * @return array
  362. */
  363. public function getResources()
  364. {
  365. $resources = array();
  366. $resDictionary = $this->_dictionary->Resources;
  367. foreach ($resDictionary->getKeys() as $resType) {
  368. $resources[$resType] = array();
  369. if ($resType == 'ProcSet') {
  370. foreach ($resDictionary->ProcSet->items as $procSetEntry) {
  371. $resources[$resType][] = $procSetEntry->value;
  372. }
  373. } else {
  374. $resMap = $resDictionary->$resType;
  375. foreach ($resMap->getKeys() as $resId) {
  376. $resources[$resType][$resId] =new Zend_Pdf_Resource_Unified($resMap->$resId);
  377. }
  378. }
  379. }
  380. return $resources;
  381. }
  382. /**
  383. * Get drawing instructions stream
  384. *
  385. * It has to be returned as a PDF stream object to make it reusable.
  386. *
  387. * @internal
  388. * @returns Zend_Pdf_Resource_ContentStream
  389. */
  390. public function getContents()
  391. {
  392. /** @todo implementation */
  393. }
  394. /**
  395. * Return the height of this page in points.
  396. *
  397. * @return float
  398. */
  399. public function getHeight()
  400. {
  401. return $this->_dictionary->MediaBox->items[3]->value -
  402. $this->_dictionary->MediaBox->items[1]->value;
  403. }
  404. /**
  405. * Return the width of this page in points.
  406. *
  407. * @return float
  408. */
  409. public function getWidth()
  410. {
  411. return $this->_dictionary->MediaBox->items[2]->value -
  412. $this->_dictionary->MediaBox->items[0]->value;
  413. }
  414. /**
  415. * Clone page, extract it and dependent objects from the current document,
  416. * so it can be used within other docs.
  417. */
  418. public function __clone()
  419. {
  420. $factory = Zend_Pdf_ElementFactory::createFactory(1);
  421. $processed = array();
  422. // Clone dictionary object.
  423. // Do it explicitly to prevent sharing page attributes between different
  424. // results of clonePage() operation (other resources are still shared)
  425. $dictionary = new Zend_Pdf_Element_Dictionary();
  426. foreach ($this->_dictionary->getKeys() as $key) {
  427. $dictionary->$key = $this->_dictionary->$key->makeClone($factory->getFactory(),
  428. $processed,
  429. Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES);
  430. }
  431. $this->_dictionary = $factory->newObject($dictionary);
  432. $this->_objFactory = $factory;
  433. $this->_attached = false;
  434. $this->_style = null;
  435. $this->_font = null;
  436. }
  437. /**
  438. * Clone page, extract it and dependent objects from the current document,
  439. * so it can be used within other docs.
  440. *
  441. * @internal
  442. * @param Zend_Pdf_ElementFactory_Interface $factory
  443. * @param array $processed
  444. * @return Zend_Pdf_Page
  445. */
  446. public function clonePage($factory, &$processed)
  447. {
  448. // Clone dictionary object.
  449. // Do it explicitly to prevent sharing page attributes between different
  450. // results of clonePage() operation (other resources are still shared)
  451. $dictionary = new Zend_Pdf_Element_Dictionary();
  452. foreach ($this->_dictionary->getKeys() as $key) {
  453. $dictionary->$key = $this->_dictionary->$key->makeClone($factory->getFactory(),
  454. $processed,
  455. Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES);
  456. }
  457. $clonedPage = new Zend_Pdf_Page($factory->newObject($dictionary), $factory);
  458. $clonedPage->_attached = false;
  459. return $clonedPage;
  460. }
  461. /**
  462. * Retrive PDF file reference to the page
  463. *
  464. * @internal
  465. * @return Zend_Pdf_Element_Dictionary
  466. */
  467. public function getPageDictionary()
  468. {
  469. return $this->_dictionary;
  470. }
  471. /**
  472. * Dump current drawing instructions into the content stream.
  473. *
  474. * @todo Don't forget to close all current graphics operations (like path drawing)
  475. *
  476. * @throws Zend_Pdf_Exception
  477. */
  478. public function flush()
  479. {
  480. if ($this->_saveCount != 0) {
  481. // require_once 'Zend/Pdf/Exception.php';
  482. throw new Zend_Pdf_Exception('Saved graphics state is not restored');
  483. }
  484. if ($this->_contents == '') {
  485. return;
  486. }
  487. if ($this->_dictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) {
  488. /**
  489. * It's a stream object.
  490. * Prepare Contents page attribute for update.
  491. */
  492. $this->_dictionary->touch();
  493. $currentPageContents = $this->_dictionary->Contents;
  494. $this->_dictionary->Contents = new Zend_Pdf_Element_Array();
  495. $this->_dictionary->Contents->items[] = $currentPageContents;
  496. } else {
  497. $this->_dictionary->Contents->touch();
  498. }
  499. if ((!$this->_safeGS) && (count($this->_dictionary->Contents->items) != 0)) {
  500. /**
  501. * Page already has some content which is not treated as safe.
  502. *
  503. * Add save/restore GS operators
  504. */
  505. $this->_addProcSet('PDF');
  506. $newContentsArray = new Zend_Pdf_Element_Array();
  507. $newContentsArray->items[] = $this->_objFactory->newStreamObject(" q\n");
  508. foreach ($this->_dictionary->Contents->items as $contentStream) {
  509. $newContentsArray->items[] = $contentStream;
  510. }
  511. $newContentsArray->items[] = $this->_objFactory->newStreamObject(" Q\n");
  512. $this->_dictionary->touch();
  513. $this->_dictionary->Contents = $newContentsArray;
  514. $this->_safeGS = true;
  515. }
  516. $this->_dictionary->Contents->items[] =
  517. $this->_objFactory->newStreamObject($this->_contents);
  518. $this->_contents = '';
  519. }
  520. /**
  521. * Prepare page to be rendered into PDF.
  522. *
  523. * @todo Don't forget to close all current graphics operations (like path drawing)
  524. *
  525. * @param Zend_Pdf_ElementFactory_Interface $objFactory
  526. * @throws Zend_Pdf_Exception
  527. */
  528. public function render(Zend_Pdf_ElementFactory_Interface $objFactory)
  529. {
  530. $this->flush();
  531. if ($objFactory === $this->_objFactory) {
  532. // Page is already attached to the document.
  533. return;
  534. }
  535. if ($this->_attached) {
  536. // require_once 'Zend/Pdf/Exception.php';
  537. throw new Zend_Pdf_Exception('Page is attached to other documen. Use clone $page to get it context free.');
  538. } else {
  539. $objFactory->attach($this->_objFactory);
  540. }
  541. }
  542. /**
  543. * Extract resources attached to the page
  544. *
  545. * This method is not intended to be used in userland, but helps to optimize some document wide operations
  546. *
  547. * returns array of Zend_Pdf_Element_Dictionary objects
  548. *
  549. * @internal
  550. * @return array
  551. */
  552. public function extractResources()
  553. {
  554. return $this->_dictionary->Resources;
  555. }
  556. /**
  557. * Extract fonts attached to the page
  558. *
  559. * returns array of Zend_Pdf_Resource_Font_Extracted objects
  560. *
  561. * @return array
  562. * @throws Zend_Pdf_Exception
  563. */
  564. public function extractFonts()
  565. {
  566. if ($this->_dictionary->Resources->Font === null) {
  567. // Page doesn't have any font attached
  568. // Return empty array
  569. return array();
  570. }
  571. $fontResources = $this->_dictionary->Resources->Font;
  572. $fontResourcesUnique = array();
  573. foreach ($fontResources->getKeys() as $fontResourceName) {
  574. $fontDictionary = $fontResources->$fontResourceName;
  575. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  576. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  577. // require_once 'Zend/Pdf/Exception.php';
  578. throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
  579. }
  580. $fontResourcesUnique[spl_object_hash($fontDictionary->getObject())] = $fontDictionary;
  581. }
  582. $fonts = array();
  583. // require_once 'Zend/Pdf/Exception.php';
  584. foreach ($fontResourcesUnique as $resourceId => $fontDictionary) {
  585. try {
  586. // require_once 'Zend/Pdf/Resource/Font/Extracted.php';
  587. // Try to extract font
  588. $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  589. $fonts[$resourceId] = $extractedFont;
  590. } catch (Zend_Pdf_Exception $e) {
  591. if ($e->getMessage() != 'Unsupported font type.') {
  592. throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e);
  593. }
  594. }
  595. }
  596. return $fonts;
  597. }
  598. /**
  599. * Extract font attached to the page by specific font name
  600. *
  601. * $fontName should be specified in UTF-8 encoding
  602. *
  603. * @return Zend_Pdf_Resource_Font_Extracted|null
  604. * @throws Zend_Pdf_Exception
  605. */
  606. public function extractFont($fontName)
  607. {
  608. if ($this->_dictionary->Resources->Font === null) {
  609. // Page doesn't have any font attached
  610. return null;
  611. }
  612. $fontResources = $this->_dictionary->Resources->Font;
  613. $fontResourcesUnique = array();
  614. // require_once 'Zend/Pdf/Exception.php';
  615. foreach ($fontResources->getKeys() as $fontResourceName) {
  616. $fontDictionary = $fontResources->$fontResourceName;
  617. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  618. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  619. // require_once 'Zend/Pdf/Exception.php';
  620. throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
  621. }
  622. $resourceId = spl_object_hash($fontDictionary->getObject());
  623. if (isset($fontResourcesUnique[$resourceId])) {
  624. continue;
  625. } else {
  626. // Mark resource as processed
  627. $fontResourcesUnique[$resourceId] = 1;
  628. }
  629. if ($fontDictionary->BaseFont->value != $fontName) {
  630. continue;
  631. }
  632. try {
  633. // Try to extract font
  634. // require_once 'Zend/Pdf/Resource/Font/Extracted.php';
  635. return new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  636. } catch (Zend_Pdf_Exception $e) {
  637. if ($e->getMessage() != 'Unsupported font type.') {
  638. throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e);
  639. }
  640. // Continue searhing font with specified name
  641. }
  642. }
  643. return null;
  644. }
  645. /**
  646. *
  647. * @param Zend_Pdf_Annotation $annotation
  648. * @return Zend_Pdf_Page
  649. */
  650. public function attachAnnotation(Zend_Pdf_Annotation $annotation)
  651. {
  652. $annotationDictionary = $annotation->getResource();
  653. if (!$annotationDictionary instanceof Zend_Pdf_Element_Object &&
  654. !$annotationDictionary instanceof Zend_Pdf_Element_Reference) {
  655. $annotationDictionary = $this->_objFactory->newObject($annotationDictionary);
  656. }
  657. if ($this->_dictionary->Annots === null) {
  658. $this->_dictionary->touch();
  659. $this->_dictionary->Annots = new Zend_Pdf_Element_Array();
  660. } else {
  661. $this->_dictionary->Annots->touch();
  662. }
  663. $this->_dictionary->Annots->items[] = $annotationDictionary;
  664. $annotationDictionary->touch();
  665. $annotationDictionary->P = $this->_dictionary;
  666. return $this;
  667. }
  668. }