Page.php 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827
  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-2010 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. /**
  32. * PDF Page
  33. *
  34. * @package Zend_Pdf
  35. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class Zend_Pdf_Page
  39. {
  40. /**** Class Constants ****/
  41. /* Page Sizes */
  42. /**
  43. * Size representing an A4 page in portrait (tall) orientation.
  44. */
  45. const SIZE_A4 = '595:842:';
  46. /**
  47. * Size representing an A4 page in landscape (wide) orientation.
  48. */
  49. const SIZE_A4_LANDSCAPE = '842:595:';
  50. /**
  51. * Size representing a US Letter page in portrait (tall) orientation.
  52. */
  53. const SIZE_LETTER = '612:792:';
  54. /**
  55. * Size representing a US Letter page in landscape (wide) orientation.
  56. */
  57. const SIZE_LETTER_LANDSCAPE = '792:612:';
  58. /* Shape Drawing */
  59. /**
  60. * Stroke the path only. Do not fill.
  61. */
  62. const SHAPE_DRAW_STROKE = 0;
  63. /**
  64. * Fill the path only. Do not stroke.
  65. */
  66. const SHAPE_DRAW_FILL = 1;
  67. /**
  68. * Fill and stroke the path.
  69. */
  70. const SHAPE_DRAW_FILL_AND_STROKE = 2;
  71. /* Shape Filling Methods */
  72. /**
  73. * Fill the path using the non-zero winding rule.
  74. */
  75. const FILL_METHOD_NON_ZERO_WINDING = 0;
  76. /**
  77. * Fill the path using the even-odd rule.
  78. */
  79. const FILL_METHOD_EVEN_ODD = 1;
  80. /* Line Dash Types */
  81. /**
  82. * Solid line dash.
  83. */
  84. const LINE_DASHING_SOLID = 0;
  85. /**
  86. * Page dictionary (refers to an inderect Zend_Pdf_Element_Dictionary object).
  87. *
  88. * @var Zend_Pdf_Element_Reference|Zend_Pdf_Element_Object
  89. */
  90. protected $_pageDictionary;
  91. /**
  92. * PDF objects factory.
  93. *
  94. * @var Zend_Pdf_ElementFactory_Interface
  95. */
  96. protected $_objFactory = null;
  97. /**
  98. * Flag which signals, that page is created separately from any PDF document or
  99. * attached to anyone.
  100. *
  101. * @var boolean
  102. */
  103. protected $_attached;
  104. /**
  105. * Stream of the drawing instructions.
  106. *
  107. * @var string
  108. */
  109. protected $_contents = '';
  110. /**
  111. * Current style
  112. *
  113. * @var Zend_Pdf_Style
  114. */
  115. protected $_style = null;
  116. /**
  117. * Counter for the "Save" operations
  118. *
  119. * @var integer
  120. */
  121. protected $_saveCount = 0;
  122. /**
  123. * Safe Graphics State semafore
  124. *
  125. * If it's false, than we can't be sure Graphics State is restored withing
  126. * context of previous contents stream (ex. drawing coordinate system may be rotated).
  127. * We should encompass existing content with save/restore GS operators
  128. *
  129. * @var boolean
  130. */
  131. protected $_safeGS;
  132. /**
  133. * Current font
  134. *
  135. * @var Zend_Pdf_Resource_Font
  136. */
  137. protected $_font = null;
  138. /**
  139. * Current font size
  140. *
  141. * @var float
  142. */
  143. protected $_fontSize;
  144. /**
  145. * Object constructor.
  146. * Constructor signatures:
  147. *
  148. * 1. Load PDF page from a parsed PDF file.
  149. * Object factory is created by PDF parser.
  150. * ---------------------------------------------------------
  151. * new Zend_Pdf_Page(Zend_Pdf_Element_Dictionary $pageDict,
  152. * Zend_Pdf_ElementFactory_Interface $factory);
  153. * ---------------------------------------------------------
  154. *
  155. * 2. Make a copy of the PDF page.
  156. * New page is created in the same context as source page. Object factory is shared.
  157. * Thus it will be attached to the document, but need to be placed into Zend_Pdf::$pages array
  158. * to be included into output.
  159. * ---------------------------------------------------------
  160. * new Zend_Pdf_Page(Zend_Pdf_Page $page);
  161. * ---------------------------------------------------------
  162. *
  163. * 3. Create new page with a specified pagesize.
  164. * If $factory is null then it will be created and page must be attached to the document to be
  165. * included into output.
  166. * ---------------------------------------------------------
  167. * new Zend_Pdf_Page(string $pagesize, Zend_Pdf_ElementFactory_Interface $factory = null);
  168. * ---------------------------------------------------------
  169. *
  170. * 4. Create new page with a specified pagesize (in default user space units).
  171. * If $factory is null then it will be created and page must be attached to the document to be
  172. * included into output.
  173. * ---------------------------------------------------------
  174. * new Zend_Pdf_Page(numeric $width, numeric $height, Zend_Pdf_ElementFactory_Interface $factory = null);
  175. * ---------------------------------------------------------
  176. *
  177. *
  178. * @param mixed $param1
  179. * @param mixed $param2
  180. * @param mixed $param3
  181. * @throws Zend_Pdf_Exception
  182. */
  183. public function __construct($param1, $param2 = null, $param3 = null)
  184. {
  185. if (($param1 instanceof Zend_Pdf_Element_Reference ||
  186. $param1 instanceof Zend_Pdf_Element_Object
  187. ) &&
  188. $param2 instanceof Zend_Pdf_ElementFactory_Interface &&
  189. $param3 === null
  190. ) {
  191. switch ($param1->getType()) {
  192. case Zend_Pdf_Element::TYPE_DICTIONARY:
  193. $this->_pageDictionary = $param1;
  194. $this->_objFactory = $param2;
  195. $this->_attached = true;
  196. $this->_safeGS = false;
  197. return;
  198. break;
  199. case Zend_Pdf_Element::TYPE_NULL:
  200. $this->_objFactory = $param2;
  201. $pageWidth = $pageHeight = 0;
  202. break;
  203. default:
  204. require_once 'Zend/Pdf/Exception.php';
  205. throw new Zend_Pdf_Exception('Unrecognized object type.');
  206. break;
  207. }
  208. } else if ($param1 instanceof Zend_Pdf_Page && $param2 === null && $param3 === null) {
  209. // Clone existing page.
  210. // Let already existing content and resources to be shared between pages
  211. // We don't give existing content modification functionality, so we don't need "deep copy"
  212. $this->_objFactory = $param1->_objFactory;
  213. $this->_attached = &$param1->_attached;
  214. $this->_safeGS = false;
  215. $this->_pageDictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  216. foreach ($param1->_pageDictionary->getKeys() as $key) {
  217. if ($key == 'Contents') {
  218. // Clone Contents property
  219. $this->_pageDictionary->Contents = new Zend_Pdf_Element_Array();
  220. if ($param1->_pageDictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) {
  221. // Prepare array of content streams and add existing stream
  222. $this->_pageDictionary->Contents->items[] = $param1->_pageDictionary->Contents;
  223. } else {
  224. // Clone array of the content streams
  225. foreach ($param1->_pageDictionary->Contents->items as $srcContentStream) {
  226. $this->_pageDictionary->Contents->items[] = $srcContentStream;
  227. }
  228. }
  229. } else {
  230. $this->_pageDictionary->$key = $param1->_pageDictionary->$key;
  231. }
  232. }
  233. return;
  234. } else if (is_string($param1) &&
  235. ($param2 === null || $param2 instanceof Zend_Pdf_ElementFactory_Interface) &&
  236. $param3 === null) {
  237. if ($param2 !== null) {
  238. $this->_objFactory = $param2;
  239. } else {
  240. require_once 'Zend/Pdf/ElementFactory.php';
  241. $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
  242. }
  243. $this->_attached = false;
  244. $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */
  245. switch (strtolower($param1)) {
  246. case 'a4':
  247. $param1 = Zend_Pdf_Page::SIZE_A4;
  248. break;
  249. case 'a4-landscape':
  250. $param1 = Zend_Pdf_Page::SIZE_A4_LANDSCAPE;
  251. break;
  252. case 'letter':
  253. $param1 = Zend_Pdf_Page::SIZE_LETTER;
  254. break;
  255. case 'letter-landscape':
  256. $param1 = Zend_Pdf_Page::SIZE_LETTER_LANDSCAPE;
  257. break;
  258. default:
  259. // should be in "x:y" or "x:y:" form
  260. }
  261. $pageDim = explode(':', $param1);
  262. if(count($pageDim) == 2 || count($pageDim) == 3) {
  263. $pageWidth = $pageDim[0];
  264. $pageHeight = $pageDim[1];
  265. } else {
  266. /**
  267. * @todo support of user defined pagesize notations, like:
  268. * "210x297mm", "595x842", "8.5x11in", "612x792"
  269. */
  270. require_once 'Zend/Pdf/Exception.php';
  271. throw new Zend_Pdf_Exception('Wrong pagesize notation.');
  272. }
  273. /**
  274. * @todo support of pagesize recalculation to "default user space units"
  275. */
  276. } else if (is_numeric($param1) && is_numeric($param2) &&
  277. ($param3 === null || $param3 instanceof Zend_Pdf_ElementFactory_Interface)) {
  278. if ($param3 !== null) {
  279. $this->_objFactory = $param3;
  280. } else {
  281. require_once 'Zend/Pdf/ElementFactory.php';
  282. $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
  283. }
  284. $this->_attached = false;
  285. $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */
  286. $pageWidth = $param1;
  287. $pageHeight = $param2;
  288. } else {
  289. require_once 'Zend/Pdf/Exception.php';
  290. throw new Zend_Pdf_Exception('Unrecognized method signature, wrong number of arguments or wrong argument types.');
  291. }
  292. $this->_pageDictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
  293. $this->_pageDictionary->Type = new Zend_Pdf_Element_Name('Page');
  294. require_once 'Zend/Pdf.php';
  295. $this->_pageDictionary->LastModified = new Zend_Pdf_Element_String(Zend_Pdf::pdfDate());
  296. $this->_pageDictionary->Resources = new Zend_Pdf_Element_Dictionary();
  297. $this->_pageDictionary->MediaBox = new Zend_Pdf_Element_Array();
  298. $this->_pageDictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0);
  299. $this->_pageDictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0);
  300. $this->_pageDictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageWidth);
  301. $this->_pageDictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageHeight);
  302. $this->_pageDictionary->Contents = new Zend_Pdf_Element_Array();
  303. }
  304. /**
  305. * Attach resource to the page
  306. *
  307. * @param string $type
  308. * @param Zend_Pdf_Resource $resource
  309. * @return string
  310. */
  311. protected function _attachResource($type, Zend_Pdf_Resource $resource)
  312. {
  313. // Check that Resources dictionary contains appropriate resource set
  314. if ($this->_pageDictionary->Resources->$type === null) {
  315. $this->_pageDictionary->Resources->touch();
  316. $this->_pageDictionary->Resources->$type = new Zend_Pdf_Element_Dictionary();
  317. } else {
  318. $this->_pageDictionary->Resources->$type->touch();
  319. }
  320. // Check, that resource is already attached to resource set.
  321. $resObject = $resource->getResource();
  322. foreach ($this->_pageDictionary->Resources->$type->getKeys() as $ResID) {
  323. if ($this->_pageDictionary->Resources->$type->$ResID === $resObject) {
  324. return $ResID;
  325. }
  326. }
  327. $idCounter = 1;
  328. do {
  329. $newResName = $type[0] . $idCounter++;
  330. } while ($this->_pageDictionary->Resources->$type->$newResName !== null);
  331. $this->_pageDictionary->Resources->$type->$newResName = $resObject;
  332. $this->_objFactory->attach($resource->getFactory());
  333. return $newResName;
  334. }
  335. /**
  336. * Add procedureSet to the Page description
  337. *
  338. * @param string $procSetName
  339. */
  340. protected function _addProcSet($procSetName)
  341. {
  342. // Check that Resources dictionary contains ProcSet entry
  343. if ($this->_pageDictionary->Resources->ProcSet === null) {
  344. $this->_pageDictionary->Resources->touch();
  345. $this->_pageDictionary->Resources->ProcSet = new Zend_Pdf_Element_Array();
  346. } else {
  347. $this->_pageDictionary->Resources->ProcSet->touch();
  348. }
  349. foreach ($this->_pageDictionary->Resources->ProcSet->items as $procSetEntry) {
  350. if ($procSetEntry->value == $procSetName) {
  351. // Procset is already included into a ProcSet array
  352. return;
  353. }
  354. }
  355. $this->_pageDictionary->Resources->ProcSet->items[] = new Zend_Pdf_Element_Name($procSetName);
  356. }
  357. /**
  358. * Clone page, extract it and dependent objects from the current document,
  359. * so it can be used within other docs.
  360. */
  361. public function __clone()
  362. {
  363. $factory = Zend_Pdf_ElementFactory::createFactory(1);
  364. $processed = array();
  365. // Clone dictionary object.
  366. // Do it explicitly to prevent sharing page attributes between different
  367. // results of clonePage() operation (other resources are still shared)
  368. $dictionary = new Zend_Pdf_Element_Dictionary();
  369. foreach ($this->_pageDictionary->getKeys() as $key) {
  370. $dictionary->$key = $this->_pageDictionary->$key->makeClone($factory->getFactory(),
  371. $processed,
  372. Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES);
  373. }
  374. $this->_pageDictionary = $factory->newObject($dictionary);
  375. $this->_objFactory = $factory;
  376. $this->_attached = false;
  377. $this->_style = null;
  378. $this->_font = null;
  379. }
  380. /**
  381. * Clone page, extract it and dependent objects from the current document,
  382. * so it can be used within other docs.
  383. *
  384. * @internal
  385. * @param Zend_Pdf_ElementFactory_Interface $factory
  386. * @param array $processed
  387. * @return Zend_Pdf_Page
  388. */
  389. public function clonePage($factory, &$processed)
  390. {
  391. // Clone dictionary object.
  392. // Do it explicitly to prevent sharing page attributes between different
  393. // results of clonePage() operation (other resources are still shared)
  394. $dictionary = new Zend_Pdf_Element_Dictionary();
  395. foreach ($this->_pageDictionary->getKeys() as $key) {
  396. $dictionary->$key = $this->_pageDictionary->$key->makeClone($factory->getFactory(),
  397. $processed,
  398. Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES);
  399. }
  400. $clonedPage = new Zend_Pdf_Page($factory->newObject($dictionary), $factory);
  401. $clonedPage->_attached = false;
  402. return $clonedPage;
  403. }
  404. /**
  405. * Retrive PDF file reference to the page
  406. *
  407. * @internal
  408. * @return Zend_Pdf_Element_Dictionary
  409. */
  410. public function getPageDictionary()
  411. {
  412. return $this->_pageDictionary;
  413. }
  414. /**
  415. * Dump current drawing instructions into the content stream.
  416. *
  417. * @todo Don't forget to close all current graphics operations (like path drawing)
  418. *
  419. * @throws Zend_Pdf_Exception
  420. */
  421. public function flush()
  422. {
  423. if ($this->_saveCount != 0) {
  424. require_once 'Zend/Pdf/Exception.php';
  425. throw new Zend_Pdf_Exception('Saved graphics state is not restored');
  426. }
  427. if ($this->_contents == '') {
  428. return;
  429. }
  430. if ($this->_pageDictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) {
  431. /**
  432. * It's a stream object.
  433. * Prepare Contents page attribute for update.
  434. */
  435. $this->_pageDictionary->touch();
  436. $currentPageContents = $this->_pageDictionary->Contents;
  437. $this->_pageDictionary->Contents = new Zend_Pdf_Element_Array();
  438. $this->_pageDictionary->Contents->items[] = $currentPageContents;
  439. } else {
  440. $this->_pageDictionary->Contents->touch();
  441. }
  442. if ((!$this->_safeGS) && (count($this->_pageDictionary->Contents->items) != 0)) {
  443. /**
  444. * Page already has some content which is not treated as safe.
  445. *
  446. * Add save/restore GS operators
  447. */
  448. $this->_addProcSet('PDF');
  449. $newContentsArray = new Zend_Pdf_Element_Array();
  450. $newContentsArray->items[] = $this->_objFactory->newStreamObject(" q\n");
  451. foreach ($this->_pageDictionary->Contents->items as $contentStream) {
  452. $newContentsArray->items[] = $contentStream;
  453. }
  454. $newContentsArray->items[] = $this->_objFactory->newStreamObject(" Q\n");
  455. $this->_pageDictionary->touch();
  456. $this->_pageDictionary->Contents = $newContentsArray;
  457. $this->_safeGS = true;
  458. }
  459. $this->_pageDictionary->Contents->items[] =
  460. $this->_objFactory->newStreamObject($this->_contents);
  461. $this->_contents = '';
  462. }
  463. /**
  464. * Prepare page to be rendered into PDF.
  465. *
  466. * @todo Don't forget to close all current graphics operations (like path drawing)
  467. *
  468. * @param Zend_Pdf_ElementFactory_Interface $objFactory
  469. * @throws Zend_Pdf_Exception
  470. */
  471. public function render(Zend_Pdf_ElementFactory_Interface $objFactory)
  472. {
  473. $this->flush();
  474. if ($objFactory === $this->_objFactory) {
  475. // Page is already attached to the document.
  476. return;
  477. }
  478. if ($this->_attached) {
  479. require_once 'Zend/Pdf/Exception.php';
  480. throw new Zend_Pdf_Exception('Page is attached to other documen. Use clone $page to get it context free.');
  481. } else {
  482. $objFactory->attach($this->_objFactory);
  483. }
  484. }
  485. /**
  486. * Set fill color.
  487. *
  488. * @param Zend_Pdf_Color $color
  489. * @return Zend_Pdf_Page
  490. */
  491. public function setFillColor(Zend_Pdf_Color $color)
  492. {
  493. $this->_addProcSet('PDF');
  494. $this->_contents .= $color->instructions(false);
  495. return $this;
  496. }
  497. /**
  498. * Set line color.
  499. *
  500. * @param Zend_Pdf_Color $color
  501. * @return Zend_Pdf_Page
  502. */
  503. public function setLineColor(Zend_Pdf_Color $color)
  504. {
  505. $this->_addProcSet('PDF');
  506. $this->_contents .= $color->instructions(true);
  507. return $this;
  508. }
  509. /**
  510. * Set line width.
  511. *
  512. * @param float $width
  513. * @return Zend_Pdf_Page
  514. */
  515. public function setLineWidth($width)
  516. {
  517. $this->_addProcSet('PDF');
  518. $widthObj = new Zend_Pdf_Element_Numeric($width);
  519. $this->_contents .= $widthObj->toString() . " w\n";
  520. return $this;
  521. }
  522. /**
  523. * Set line dashing pattern
  524. *
  525. * Pattern is an array of floats: array(on_length, off_length, on_length, off_length, ...)
  526. * Phase is shift from the beginning of line.
  527. *
  528. * @param array $pattern
  529. * @param array $phase
  530. * @return Zend_Pdf_Page
  531. */
  532. public function setLineDashingPattern($pattern, $phase = 0)
  533. {
  534. $this->_addProcSet('PDF');
  535. if ($pattern === Zend_Pdf_Page::LINE_DASHING_SOLID) {
  536. $pattern = array();
  537. $phase = 0;
  538. }
  539. $dashPattern = new Zend_Pdf_Element_Array();
  540. $phaseEleemnt = new Zend_Pdf_Element_Numeric($phase);
  541. foreach ($pattern as $dashItem) {
  542. $dashElement = new Zend_Pdf_Element_Numeric($dashItem);
  543. $dashPattern->items[] = $dashElement;
  544. }
  545. $this->_contents .= $dashPattern->toString() . ' '
  546. . $phaseEleemnt->toString() . " d\n";
  547. return $this;
  548. }
  549. /**
  550. * Set current font.
  551. *
  552. * @param Zend_Pdf_Resource_Font $font
  553. * @param float $fontSize
  554. * @return Zend_Pdf_Page
  555. */
  556. public function setFont(Zend_Pdf_Resource_Font $font, $fontSize)
  557. {
  558. $this->_addProcSet('Text');
  559. $fontName = $this->_attachResource('Font', $font);
  560. $this->_font = $font;
  561. $this->_fontSize = $fontSize;
  562. $fontNameObj = new Zend_Pdf_Element_Name($fontName);
  563. $fontSizeObj = new Zend_Pdf_Element_Numeric($fontSize);
  564. $this->_contents .= $fontNameObj->toString() . ' ' . $fontSizeObj->toString() . " Tf\n";
  565. return $this;
  566. }
  567. /**
  568. * Set the style to use for future drawing operations on this page
  569. *
  570. * @param Zend_Pdf_Style $style
  571. * @return Zend_Pdf_Page
  572. */
  573. public function setStyle(Zend_Pdf_Style $style)
  574. {
  575. $this->_style = $style;
  576. $this->_addProcSet('Text');
  577. $this->_addProcSet('PDF');
  578. if ($style->getFont() !== null) {
  579. $this->setFont($style->getFont(), $style->getFontSize());
  580. }
  581. $this->_contents .= $style->instructions($this->_pageDictionary->Resources);
  582. return $this;
  583. }
  584. /**
  585. * Set the transparancy
  586. *
  587. * $alpha == 0 - transparent
  588. * $alpha == 1 - opaque
  589. *
  590. * Transparency modes, supported by PDF:
  591. * Normal (default), Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight,
  592. * SoftLight, Difference, Exclusion
  593. *
  594. * @param float $alpha
  595. * @param string $mode
  596. * @throws Zend_Pdf_Exception
  597. * @return Zend_Pdf_Page
  598. */
  599. public function setAlpha($alpha, $mode = 'Normal')
  600. {
  601. if (!in_array($mode, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge',
  602. 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion'))) {
  603. require_once 'Zend/Pdf/Exception.php';
  604. throw new Zend_Pdf_Exception('Unsupported transparency mode.');
  605. }
  606. if (!is_numeric($alpha) || $alpha < 0 || $alpha > 1) {
  607. require_once 'Zend/Pdf/Exception.php';
  608. throw new Zend_Pdf_Exception('Alpha value must be numeric between 0 (transparent) and 1 (opaque).');
  609. }
  610. $this->_addProcSet('Text');
  611. $this->_addProcSet('PDF');
  612. $resources = $this->_pageDictionary->Resources;
  613. // Check if Resources dictionary contains ExtGState entry
  614. if ($resources->ExtGState === null) {
  615. $resources->touch();
  616. $resources->ExtGState = new Zend_Pdf_Element_Dictionary();
  617. } else {
  618. $resources->ExtGState->touch();
  619. }
  620. $idCounter = 1;
  621. do {
  622. $gStateName = 'GS' . $idCounter++;
  623. } while ($resources->ExtGState->$gStateName !== null);
  624. $gStateDictionary = new Zend_Pdf_Element_Dictionary();
  625. $gStateDictionary->Type = new Zend_Pdf_Element_Name('ExtGState');
  626. $gStateDictionary->BM = new Zend_Pdf_Element_Name($mode);
  627. $gStateDictionary->CA = new Zend_Pdf_Element_Numeric($alpha);
  628. $gStateDictionary->ca = new Zend_Pdf_Element_Numeric($alpha);
  629. $resources->ExtGState->$gStateName = $this->_objFactory->newObject($gStateDictionary);
  630. $gStateNameObj = new Zend_Pdf_Element_Name($gStateName);
  631. $this->_contents .= $gStateNameObj->toString() . " gs\n";
  632. return $this;
  633. }
  634. /**
  635. * Get current font.
  636. *
  637. * @return Zend_Pdf_Resource_Font $font
  638. */
  639. public function getFont()
  640. {
  641. return $this->_font;
  642. }
  643. /**
  644. * Extract resources attached to the page
  645. *
  646. * This method is not intended to be used in userland, but helps to optimize some document wide operations
  647. *
  648. * returns array of Zend_Pdf_Element_Dictionary objects
  649. *
  650. * @internal
  651. * @return array
  652. */
  653. public function extractResources()
  654. {
  655. return $this->_pageDictionary->Resources;
  656. }
  657. /**
  658. * Extract fonts attached to the page
  659. *
  660. * returns array of Zend_Pdf_Resource_Font_Extracted objects
  661. *
  662. * @return array
  663. * @throws Zend_Pdf_Exception
  664. */
  665. public function extractFonts()
  666. {
  667. if ($this->_pageDictionary->Resources->Font === null) {
  668. // Page doesn't have any font attached
  669. // Return empty array
  670. return array();
  671. }
  672. $fontResources = $this->_pageDictionary->Resources->Font;
  673. $fontResourcesUnique = array();
  674. foreach ($fontResources->getKeys() as $fontResourceName) {
  675. $fontDictionary = $fontResources->$fontResourceName;
  676. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  677. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  678. require_once 'Zend/Pdf/Exception.php';
  679. throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
  680. }
  681. $fontResourcesUnique[spl_object_hash($fontDictionary->getObject())] = $fontDictionary;
  682. }
  683. $fonts = array();
  684. require_once 'Zend/Pdf/Exception.php';
  685. foreach ($fontResourcesUnique as $resourceId => $fontDictionary) {
  686. try {
  687. require_once 'Zend/Pdf/Resource/Font/Extracted.php';
  688. // Try to extract font
  689. $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  690. $fonts[$resourceId] = $extractedFont;
  691. } catch (Zend_Pdf_Exception $e) {
  692. if ($e->getMessage() != 'Unsupported font type.') {
  693. throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e);
  694. }
  695. }
  696. }
  697. return $fonts;
  698. }
  699. /**
  700. * Extract font attached to the page by specific font name
  701. *
  702. * $fontName should be specified in UTF-8 encoding
  703. *
  704. * @return Zend_Pdf_Resource_Font_Extracted|null
  705. * @throws Zend_Pdf_Exception
  706. */
  707. public function extractFont($fontName)
  708. {
  709. if ($this->_pageDictionary->Resources->Font === null) {
  710. // Page doesn't have any font attached
  711. return null;
  712. }
  713. $fontResources = $this->_pageDictionary->Resources->Font;
  714. $fontResourcesUnique = array();
  715. require_once 'Zend/Pdf/Exception.php';
  716. foreach ($fontResources->getKeys() as $fontResourceName) {
  717. $fontDictionary = $fontResources->$fontResourceName;
  718. if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
  719. $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
  720. require_once 'Zend/Pdf/Exception.php';
  721. throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
  722. }
  723. $resourceId = spl_object_hash($fontDictionary->getObject());
  724. if (isset($fontResourcesUnique[$resourceId])) {
  725. continue;
  726. } else {
  727. // Mark resource as processed
  728. $fontResourcesUnique[$resourceId] = 1;
  729. }
  730. if ($fontDictionary->BaseFont->value != $fontName) {
  731. continue;
  732. }
  733. try {
  734. // Try to extract font
  735. require_once 'Zend/Pdf/Resource/Font/Extracted.php';
  736. return new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
  737. } catch (Zend_Pdf_Exception $e) {
  738. if ($e->getMessage() != 'Unsupported font type.') {
  739. throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e);
  740. }
  741. // Continue searhing font with specified name
  742. }
  743. }
  744. return null;
  745. }
  746. /**
  747. * Get current font size
  748. *
  749. * @return float $fontSize
  750. */
  751. public function getFontSize()
  752. {
  753. return $this->_fontSize;
  754. }
  755. /**
  756. * Return the style, applied to the page.
  757. *
  758. * @return Zend_Pdf_Style|null
  759. */
  760. public function getStyle()
  761. {
  762. return $this->_style;
  763. }
  764. /**
  765. * Save the graphics state of this page.
  766. * This takes a snapshot of the currently applied style, position, clipping area and
  767. * any rotation/translation/scaling that has been applied.
  768. *
  769. * @todo check for the open paths
  770. * @throws Zend_Pdf_Exception - if a save is performed with an open path
  771. * @return Zend_Pdf_Page
  772. */
  773. public function saveGS()
  774. {
  775. $this->_saveCount++;
  776. $this->_addProcSet('PDF');
  777. $this->_contents .= " q\n";
  778. return $this;
  779. }
  780. /**
  781. * Restore the graphics state that was saved with the last call to saveGS().
  782. *
  783. * @throws Zend_Pdf_Exception - if there is no previously saved state
  784. * @return Zend_Pdf_Page
  785. */
  786. public function restoreGS()
  787. {
  788. if ($this->_saveCount-- <= 0) {
  789. require_once 'Zend/Pdf/Exception.php';
  790. throw new Zend_Pdf_Exception('Restoring graphics state which is not saved');
  791. }
  792. $this->_contents .= " Q\n";
  793. return $this;
  794. }
  795. /**
  796. * Intersect current clipping area with a circle.
  797. *
  798. * @param float $x
  799. * @param float $y
  800. * @param float $radius
  801. * @param float $startAngle
  802. * @param float $endAngle
  803. * @return Zend_Pdf_Page
  804. */
  805. public function clipCircle($x, $y, $radius, $startAngle = null, $endAngle = null)
  806. {
  807. $this->clipEllipse($x - $radius, $y - $radius,
  808. $x + $radius, $y + $radius,
  809. $startAngle, $endAngle);
  810. return $this;
  811. }
  812. /**
  813. * Intersect current clipping area with a polygon.
  814. *
  815. * Method signatures:
  816. * drawEllipse($x1, $y1, $x2, $y2);
  817. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  818. *
  819. * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
  820. *
  821. * @param float $x1
  822. * @param float $y1
  823. * @param float $x2
  824. * @param float $y2
  825. * @param float $startAngle
  826. * @param float $endAngle
  827. * @return Zend_Pdf_Page
  828. */
  829. public function clipEllipse($x1, $y1, $x2, $y2, $startAngle = null, $endAngle = null)
  830. {
  831. $this->_addProcSet('PDF');
  832. if ($x2 < $x1) {
  833. $temp = $x1;
  834. $x1 = $x2;
  835. $x2 = $temp;
  836. }
  837. if ($y2 < $y1) {
  838. $temp = $y1;
  839. $y1 = $y2;
  840. $y2 = $temp;
  841. }
  842. $x = ($x1 + $x2)/2.;
  843. $y = ($y1 + $y2)/2.;
  844. $xC = new Zend_Pdf_Element_Numeric($x);
  845. $yC = new Zend_Pdf_Element_Numeric($y);
  846. if ($startAngle !== null) {
  847. if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI*2); }
  848. if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI*2); }
  849. if ($startAngle > $endAngle) {
  850. $endAngle += M_PI*2;
  851. }
  852. $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
  853. $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4);
  854. $clipRadius = max($x2 - $x1, $y2 - $y1);
  855. for($count = 0; $count <= $clipSectors; $count++) {
  856. $pAngle = $startAngle + ($endAngle - $startAngle)*$count/(float)$clipSectors;
  857. $pX = new Zend_Pdf_Element_Numeric($x + cos($pAngle)*$clipRadius);
  858. $pY = new Zend_Pdf_Element_Numeric($y + sin($pAngle)*$clipRadius);
  859. $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
  860. }
  861. $this->_contents .= $clipPath . "h\nW\nn\n";
  862. }
  863. $xLeft = new Zend_Pdf_Element_Numeric($x1);
  864. $xRight = new Zend_Pdf_Element_Numeric($x2);
  865. $yUp = new Zend_Pdf_Element_Numeric($y2);
  866. $yDown = new Zend_Pdf_Element_Numeric($y1);
  867. $xDelta = 2*(M_SQRT2 - 1)*($x2 - $x1)/3.;
  868. $yDelta = 2*(M_SQRT2 - 1)*($y2 - $y1)/3.;
  869. $xr = new Zend_Pdf_Element_Numeric($x + $xDelta);
  870. $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
  871. $yu = new Zend_Pdf_Element_Numeric($y + $yDelta);
  872. $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
  873. $this->_contents .= $xC->toString() . ' ' . $yUp->toString() . " m\n"
  874. . $xr->toString() . ' ' . $yUp->toString() . ' '
  875. . $xRight->toString() . ' ' . $yu->toString() . ' '
  876. . $xRight->toString() . ' ' . $yC->toString() . " c\n"
  877. . $xRight->toString() . ' ' . $yd->toString() . ' '
  878. . $xr->toString() . ' ' . $yDown->toString() . ' '
  879. . $xC->toString() . ' ' . $yDown->toString() . " c\n"
  880. . $xl->toString() . ' ' . $yDown->toString() . ' '
  881. . $xLeft->toString() . ' ' . $yd->toString() . ' '
  882. . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
  883. . $xLeft->toString() . ' ' . $yu->toString() . ' '
  884. . $xl->toString() . ' ' . $yUp->toString() . ' '
  885. . $xC->toString() . ' ' . $yUp->toString() . " c\n"
  886. . "h\nW\nn\n";
  887. return $this;
  888. }
  889. /**
  890. * Intersect current clipping area with a polygon.
  891. *
  892. * @param array $x - array of float (the X co-ordinates of the vertices)
  893. * @param array $y - array of float (the Y co-ordinates of the vertices)
  894. * @param integer $fillMethod
  895. * @return Zend_Pdf_Page
  896. */
  897. public function clipPolygon($x, $y, $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING)
  898. {
  899. $this->_addProcSet('PDF');
  900. $firstPoint = true;
  901. foreach ($x as $id => $xVal) {
  902. $xObj = new Zend_Pdf_Element_Numeric($xVal);
  903. $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
  904. if ($firstPoint) {
  905. $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
  906. $firstPoint = false;
  907. } else {
  908. $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
  909. }
  910. }
  911. $this->_contents .= $path;
  912. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  913. $this->_contents .= " h\n W\nn\n";
  914. } else {
  915. // Even-Odd fill method.
  916. $this->_contents .= " h\n W*\nn\n";
  917. }
  918. return $this;
  919. }
  920. /**
  921. * Intersect current clipping area with a rectangle.
  922. *
  923. * @param float $x1
  924. * @param float $y1
  925. * @param float $x2
  926. * @param float $y2
  927. * @return Zend_Pdf_Page
  928. */
  929. public function clipRectangle($x1, $y1, $x2, $y2)
  930. {
  931. $this->_addProcSet('PDF');
  932. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  933. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  934. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  935. $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  936. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  937. . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n"
  938. . " W\nn\n";
  939. return $this;
  940. }
  941. /**
  942. * Draw a Zend_Pdf_ContentStream at the specified position on the page
  943. *
  944. * @param ZPdfContentStream $cs
  945. * @param float $x1
  946. * @param float $y1
  947. * @param float $x2
  948. * @param float $y2
  949. * @return Zend_Pdf_Page
  950. */
  951. public function drawContentStream($cs, $x1, $y1, $x2, $y2)
  952. {
  953. /** @todo implementation */
  954. return $this;
  955. }
  956. /**
  957. * Draw a circle centered on x, y with a radius of radius.
  958. *
  959. * Method signatures:
  960. * drawCircle($x, $y, $radius);
  961. * drawCircle($x, $y, $radius, $fillType);
  962. * drawCircle($x, $y, $radius, $startAngle, $endAngle);
  963. * drawCircle($x, $y, $radius, $startAngle, $endAngle, $fillType);
  964. *
  965. *
  966. * It's not a really circle, because PDF supports only cubic Bezier curves.
  967. * But _very_ good approximation.
  968. * It differs from a real circle on a maximum 0.00026 radiuses
  969. * (at PI/8, 3*PI/8, 5*PI/8, 7*PI/8, 9*PI/8, 11*PI/8, 13*PI/8 and 15*PI/8 angles).
  970. * At 0, PI/4, PI/2, 3*PI/4, PI, 5*PI/4, 3*PI/2 and 7*PI/4 it's exactly a tangent to a circle.
  971. *
  972. * @param float $x
  973. * @param float $y
  974. * @param float $radius
  975. * @param mixed $param4
  976. * @param mixed $param5
  977. * @param mixed $param6
  978. * @return Zend_Pdf_Page
  979. */
  980. public function drawCircle($x, $y, $radius, $param4 = null, $param5 = null, $param6 = null)
  981. {
  982. $this->drawEllipse($x - $radius, $y - $radius,
  983. $x + $radius, $y + $radius,
  984. $param4, $param5, $param6);
  985. return $this;
  986. }
  987. /**
  988. * Draw an ellipse inside the specified rectangle.
  989. *
  990. * Method signatures:
  991. * drawEllipse($x1, $y1, $x2, $y2);
  992. * drawEllipse($x1, $y1, $x2, $y2, $fillType);
  993. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  994. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
  995. *
  996. * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
  997. *
  998. * @param float $x1
  999. * @param float $y1
  1000. * @param float $x2
  1001. * @param float $y2
  1002. * @param mixed $param5
  1003. * @param mixed $param6
  1004. * @param mixed $param7
  1005. * @return Zend_Pdf_Page
  1006. */
  1007. public function drawEllipse($x1, $y1, $x2, $y2, $param5 = null, $param6 = null, $param7 = null)
  1008. {
  1009. if ($param5 === null) {
  1010. // drawEllipse($x1, $y1, $x2, $y2);
  1011. $startAngle = null;
  1012. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE;
  1013. } else if ($param6 === null) {
  1014. // drawEllipse($x1, $y1, $x2, $y2, $fillType);
  1015. $startAngle = null;
  1016. $fillType = $param5;
  1017. } else {
  1018. // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  1019. // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
  1020. $startAngle = $param5;
  1021. $endAngle = $param6;
  1022. if ($param7 === null) {
  1023. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE;
  1024. } else {
  1025. $fillType = $param7;
  1026. }
  1027. }
  1028. $this->_addProcSet('PDF');
  1029. if ($x2 < $x1) {
  1030. $temp = $x1;
  1031. $x1 = $x2;
  1032. $x2 = $temp;
  1033. }
  1034. if ($y2 < $y1) {
  1035. $temp = $y1;
  1036. $y1 = $y2;
  1037. $y2 = $temp;
  1038. }
  1039. $x = ($x1 + $x2)/2.;
  1040. $y = ($y1 + $y2)/2.;
  1041. $xC = new Zend_Pdf_Element_Numeric($x);
  1042. $yC = new Zend_Pdf_Element_Numeric($y);
  1043. if ($startAngle !== null) {
  1044. if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI*2); }
  1045. if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI*2); }
  1046. if ($startAngle > $endAngle) {
  1047. $endAngle += M_PI*2;
  1048. }
  1049. $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
  1050. $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4);
  1051. $clipRadius = max($x2 - $x1, $y2 - $y1);
  1052. for($count = 0; $count <= $clipSectors; $count++) {
  1053. $pAngle = $startAngle + ($endAngle - $startAngle)*$count/(float)$clipSectors;
  1054. $pX = new Zend_Pdf_Element_Numeric($x + cos($pAngle)*$clipRadius);
  1055. $pY = new Zend_Pdf_Element_Numeric($y + sin($pAngle)*$clipRadius);
  1056. $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
  1057. }
  1058. $this->_contents .= "q\n" . $clipPath . "h\nW\nn\n";
  1059. }
  1060. $xLeft = new Zend_Pdf_Element_Numeric($x1);
  1061. $xRight = new Zend_Pdf_Element_Numeric($x2);
  1062. $yUp = new Zend_Pdf_Element_Numeric($y2);
  1063. $yDown = new Zend_Pdf_Element_Numeric($y1);
  1064. $xDelta = 2*(M_SQRT2 - 1)*($x2 - $x1)/3.;
  1065. $yDelta = 2*(M_SQRT2 - 1)*($y2 - $y1)/3.;
  1066. $xr = new Zend_Pdf_Element_Numeric($x + $xDelta);
  1067. $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
  1068. $yu = new Zend_Pdf_Element_Numeric($y + $yDelta);
  1069. $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
  1070. $this->_contents .= $xC->toString() . ' ' . $yUp->toString() . " m\n"
  1071. . $xr->toString() . ' ' . $yUp->toString() . ' '
  1072. . $xRight->toString() . ' ' . $yu->toString() . ' '
  1073. . $xRight->toString() . ' ' . $yC->toString() . " c\n"
  1074. . $xRight->toString() . ' ' . $yd->toString() . ' '
  1075. . $xr->toString() . ' ' . $yDown->toString() . ' '
  1076. . $xC->toString() . ' ' . $yDown->toString() . " c\n"
  1077. . $xl->toString() . ' ' . $yDown->toString() . ' '
  1078. . $xLeft->toString() . ' ' . $yd->toString() . ' '
  1079. . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
  1080. . $xLeft->toString() . ' ' . $yu->toString() . ' '
  1081. . $xl->toString() . ' ' . $yUp->toString() . ' '
  1082. . $xC->toString() . ' ' . $yUp->toString() . " c\n";
  1083. switch ($fillType) {
  1084. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  1085. $this->_contents .= " B*\n";
  1086. break;
  1087. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  1088. $this->_contents .= " f*\n";
  1089. break;
  1090. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  1091. $this->_contents .= " S\n";
  1092. break;
  1093. }
  1094. if ($startAngle !== null) {
  1095. $this->_contents .= "Q\n";
  1096. }
  1097. return $this;
  1098. }
  1099. /**
  1100. * Draw an image at the specified position on the page.
  1101. *
  1102. * @param Zend_Pdf_Image $image
  1103. * @param float $x1
  1104. * @param float $y1
  1105. * @param float $x2
  1106. * @param float $y2
  1107. * @return Zend_Pdf_Page
  1108. */
  1109. public function drawImage(Zend_Pdf_Resource_Image $image, $x1, $y1, $x2, $y2)
  1110. {
  1111. $this->_addProcSet('PDF');
  1112. $imageName = $this->_attachResource('XObject', $image);
  1113. $imageNameObj = new Zend_Pdf_Element_Name($imageName);
  1114. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  1115. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  1116. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  1117. $heightObj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  1118. $this->_contents .= "q\n"
  1119. . '1 0 0 1 ' . $x1Obj->toString() . ' ' . $y1Obj->toString() . " cm\n"
  1120. . $widthObj->toString() . ' 0 0 ' . $heightObj->toString() . " 0 0 cm\n"
  1121. . $imageNameObj->toString() . " Do\n"
  1122. . "Q\n";
  1123. return $this;
  1124. }
  1125. /**
  1126. * Draw a LayoutBox at the specified position on the page.
  1127. *
  1128. * @param Zend_Pdf_Element_LayoutBox $box
  1129. * @param float $x
  1130. * @param float $y
  1131. * @return Zend_Pdf_Page
  1132. */
  1133. public function drawLayoutBox($box, $x, $y)
  1134. {
  1135. /** @todo implementation */
  1136. return $this;
  1137. }
  1138. /**
  1139. * Draw a line from x1,y1 to x2,y2.
  1140. *
  1141. * @param float $x1
  1142. * @param float $y1
  1143. * @param float $x2
  1144. * @param float $y2
  1145. * @return Zend_Pdf_Page
  1146. */
  1147. public function drawLine($x1, $y1, $x2, $y2)
  1148. {
  1149. $this->_addProcSet('PDF');
  1150. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  1151. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  1152. $x2Obj = new Zend_Pdf_Element_Numeric($x2);
  1153. $y2Obj = new Zend_Pdf_Element_Numeric($y2);
  1154. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " m\n"
  1155. . $x2Obj->toString() . ' ' . $y2Obj->toString() . " l\n S\n";
  1156. return $this;
  1157. }
  1158. /**
  1159. * Draw a polygon.
  1160. *
  1161. * If $fillType is Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE or
  1162. * Zend_Pdf_Page::SHAPE_DRAW_FILL, then polygon is automatically closed.
  1163. * See detailed description of these methods in a PDF documentation
  1164. * (section 4.4.2 Path painting Operators, Filling)
  1165. *
  1166. * @param array $x - array of float (the X co-ordinates of the vertices)
  1167. * @param array $y - array of float (the Y co-ordinates of the vertices)
  1168. * @param integer $fillType
  1169. * @param integer $fillMethod
  1170. * @return Zend_Pdf_Page
  1171. */
  1172. public function drawPolygon($x, $y,
  1173. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE,
  1174. $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING)
  1175. {
  1176. $this->_addProcSet('PDF');
  1177. $firstPoint = true;
  1178. foreach ($x as $id => $xVal) {
  1179. $xObj = new Zend_Pdf_Element_Numeric($xVal);
  1180. $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
  1181. if ($firstPoint) {
  1182. $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
  1183. $firstPoint = false;
  1184. } else {
  1185. $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
  1186. }
  1187. }
  1188. $this->_contents .= $path;
  1189. switch ($fillType) {
  1190. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  1191. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  1192. $this->_contents .= " b\n";
  1193. } else {
  1194. // Even-Odd fill method.
  1195. $this->_contents .= " b*\n";
  1196. }
  1197. break;
  1198. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  1199. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  1200. $this->_contents .= " h\n f\n";
  1201. } else {
  1202. // Even-Odd fill method.
  1203. $this->_contents .= " h\n f*\n";
  1204. }
  1205. break;
  1206. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  1207. $this->_contents .= " S\n";
  1208. break;
  1209. }
  1210. return $this;
  1211. }
  1212. /**
  1213. * Draw a rectangle.
  1214. *
  1215. * Fill types:
  1216. * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
  1217. * Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
  1218. * Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
  1219. *
  1220. * @param float $x1
  1221. * @param float $y1
  1222. * @param float $x2
  1223. * @param float $y2
  1224. * @param integer $fillType
  1225. * @return Zend_Pdf_Page
  1226. */
  1227. public function drawRectangle($x1, $y1, $x2, $y2, $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE)
  1228. {
  1229. $this->_addProcSet('PDF');
  1230. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  1231. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  1232. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  1233. $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  1234. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  1235. . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n";
  1236. switch ($fillType) {
  1237. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  1238. $this->_contents .= " B*\n";
  1239. break;
  1240. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  1241. $this->_contents .= " f*\n";
  1242. break;
  1243. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  1244. $this->_contents .= " S\n";
  1245. break;
  1246. }
  1247. return $this;
  1248. }
  1249. /**
  1250. * Draw a rounded rectangle.
  1251. *
  1252. * Fill types:
  1253. * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
  1254. * Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
  1255. * Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
  1256. *
  1257. * radius is an integer representing radius of the four corners, or an array
  1258. * of four integers representing the radius starting at top left, going
  1259. * clockwise
  1260. *
  1261. * @param float $x1
  1262. * @param float $y1
  1263. * @param float $x2
  1264. * @param float $y2
  1265. * @param integer|array $radius
  1266. * @param integer $fillType
  1267. * @return Zend_Pdf_Page
  1268. */
  1269. public function drawRoundedRectangle($x1, $y1, $x2, $y2, $radius,
  1270. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE)
  1271. {
  1272. $this->_addProcSet('PDF');
  1273. if(!is_array($radius)) {
  1274. $radius = array($radius, $radius, $radius, $radius);
  1275. } else {
  1276. for ($i = 0; $i < 4; $i++) {
  1277. if(!isset($radius[$i])) {
  1278. $radius[$i] = 0;
  1279. }
  1280. }
  1281. }
  1282. $topLeftX = $x1;
  1283. $topLeftY = $y2;
  1284. $topRightX = $x2;
  1285. $topRightY = $y2;
  1286. $bottomRightX = $x2;
  1287. $bottomRightY = $y1;
  1288. $bottomLeftX = $x1;
  1289. $bottomLeftY = $y1;
  1290. //draw top side
  1291. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX + $radius[0]);
  1292. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  1293. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " m\n";
  1294. $x1Obj = new Zend_Pdf_Element_Numeric($topRightX - $radius[1]);
  1295. $y1Obj = new Zend_Pdf_Element_Numeric($topRightY);
  1296. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  1297. //draw top right corner if needed
  1298. if ($radius[1] != 0) {
  1299. $x1Obj = new Zend_Pdf_Element_Numeric($topRightX);
  1300. $y1Obj = new Zend_Pdf_Element_Numeric($topRightY);
  1301. $x2Obj = new Zend_Pdf_Element_Numeric($topRightX);
  1302. $y2Obj = new Zend_Pdf_Element_Numeric($topRightY);
  1303. $x3Obj = new Zend_Pdf_Element_Numeric($topRightX);
  1304. $y3Obj = new Zend_Pdf_Element_Numeric($topRightY - $radius[1]);
  1305. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  1306. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  1307. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  1308. . " c\n";
  1309. }
  1310. //draw right side
  1311. $x1Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  1312. $y1Obj = new Zend_Pdf_Element_Numeric($bottomRightY + $radius[2]);
  1313. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  1314. //draw bottom right corner if needed
  1315. if ($radius[2] != 0) {
  1316. $x1Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  1317. $y1Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  1318. $x2Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  1319. $y2Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  1320. $x3Obj = new Zend_Pdf_Element_Numeric($bottomRightX - $radius[2]);
  1321. $y3Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  1322. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  1323. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  1324. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  1325. . " c\n";
  1326. }
  1327. //draw bottom side
  1328. $x1Obj = new Zend_Pdf_Element_Numeric($bottomLeftX + $radius[3]);
  1329. $y1Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  1330. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  1331. //draw bottom left corner if needed
  1332. if ($radius[3] != 0) {
  1333. $x1Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  1334. $y1Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  1335. $x2Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  1336. $y2Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  1337. $x3Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  1338. $y3Obj = new Zend_Pdf_Element_Numeric($bottomLeftY + $radius[3]);
  1339. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  1340. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  1341. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  1342. . " c\n";
  1343. }
  1344. //draw left side
  1345. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  1346. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY - $radius[0]);
  1347. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  1348. //draw top left corner if needed
  1349. if ($radius[0] != 0) {
  1350. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  1351. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  1352. $x2Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  1353. $y2Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  1354. $x3Obj = new Zend_Pdf_Element_Numeric($topLeftX + $radius[0]);
  1355. $y3Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  1356. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  1357. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  1358. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  1359. . " c\n";
  1360. }
  1361. switch ($fillType) {
  1362. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  1363. $this->_contents .= " B*\n";
  1364. break;
  1365. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  1366. $this->_contents .= " f*\n";
  1367. break;
  1368. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  1369. $this->_contents .= " S\n";
  1370. break;
  1371. }
  1372. return $this;
  1373. }
  1374. /**
  1375. * Draw a line of text at the specified position.
  1376. *
  1377. * @param string $text
  1378. * @param float $x
  1379. * @param float $y
  1380. * @param string $charEncoding (optional) Character encoding of source text.
  1381. * Defaults to current locale.
  1382. * @throws Zend_Pdf_Exception
  1383. * @return Zend_Pdf_Page
  1384. */
  1385. public function drawText($text, $x, $y, $charEncoding = '')
  1386. {
  1387. if ($this->_font === null) {
  1388. require_once 'Zend/Pdf/Exception.php';
  1389. throw new Zend_Pdf_Exception('Font has not been set');
  1390. }
  1391. $this->_addProcSet('Text');
  1392. $textObj = new Zend_Pdf_Element_String($this->_font->encodeString($text, $charEncoding));
  1393. $xObj = new Zend_Pdf_Element_Numeric($x);
  1394. $yObj = new Zend_Pdf_Element_Numeric($y);
  1395. $this->_contents .= "BT\n"
  1396. . $xObj->toString() . ' ' . $yObj->toString() . " Td\n"
  1397. . $textObj->toString() . " Tj\n"
  1398. . "ET\n";
  1399. return $this;
  1400. }
  1401. /**
  1402. *
  1403. * @param Zend_Pdf_Annotation $annotation
  1404. * @return Zend_Pdf_Page
  1405. */
  1406. public function attachAnnotation(Zend_Pdf_Annotation $annotation)
  1407. {
  1408. $annotationDictionary = $annotation->getResource();
  1409. if (!$annotationDictionary instanceof Zend_Pdf_Element_Object &&
  1410. !$annotationDictionary instanceof Zend_Pdf_Element_Reference) {
  1411. $annotationDictionary = $this->_objFactory->newObject($annotationDictionary);
  1412. }
  1413. if ($this->_pageDictionary->Annots === null) {
  1414. $this->_pageDictionary->touch();
  1415. $this->_pageDictionary->Annots = new Zend_Pdf_Element_Array();
  1416. } else {
  1417. $this->_pageDictionary->Annots->touch();
  1418. }
  1419. $this->_pageDictionary->Annots->items[] = $annotationDictionary;
  1420. $annotationDictionary->touch();
  1421. $annotationDictionary->P = $this->_pageDictionary;
  1422. return $this;
  1423. }
  1424. /**
  1425. * Return the height of this page in points.
  1426. *
  1427. * @return float
  1428. */
  1429. public function getHeight()
  1430. {
  1431. return $this->_pageDictionary->MediaBox->items[3]->value -
  1432. $this->_pageDictionary->MediaBox->items[1]->value;
  1433. }
  1434. /**
  1435. * Return the width of this page in points.
  1436. *
  1437. * @return float
  1438. */
  1439. public function getWidth()
  1440. {
  1441. return $this->_pageDictionary->MediaBox->items[2]->value -
  1442. $this->_pageDictionary->MediaBox->items[0]->value;
  1443. }
  1444. /**
  1445. * Close the path by drawing a straight line back to it's beginning.
  1446. *
  1447. * @throws Zend_Pdf_Exception - if a path hasn't been started with pathMove()
  1448. * @return Zend_Pdf_Page
  1449. */
  1450. public function pathClose()
  1451. {
  1452. /** @todo implementation */
  1453. return $this;
  1454. }
  1455. /**
  1456. * Continue the open path in a straight line to the specified position.
  1457. *
  1458. * @param float $x - the X co-ordinate to move to
  1459. * @param float $y - the Y co-ordinate to move to
  1460. * @return Zend_Pdf_Page
  1461. */
  1462. public function pathLine($x, $y)
  1463. {
  1464. /** @todo implementation */
  1465. return $this;
  1466. }
  1467. /**
  1468. * Start a new path at the specified position. If a path has already been started,
  1469. * move the cursor without drawing a line.
  1470. *
  1471. * @param float $x - the X co-ordinate to move to
  1472. * @param float $y - the Y co-ordinate to move to
  1473. * @return Zend_Pdf_Page
  1474. */
  1475. public function pathMove($x, $y)
  1476. {
  1477. /** @todo implementation */
  1478. return $this;
  1479. }
  1480. /**
  1481. * Writes the raw data to the page's content stream.
  1482. *
  1483. * Be sure to consult the PDF reference to ensure your syntax is correct. No
  1484. * attempt is made to ensure the validity of the stream data.
  1485. *
  1486. * @param string $data
  1487. * @param string $procSet (optional) Name of ProcSet to add.
  1488. * @return Zend_Pdf_Page
  1489. */
  1490. public function rawWrite($data, $procSet = null)
  1491. {
  1492. if (! empty($procSet)) {
  1493. $this->_addProcSet($procSet);
  1494. }
  1495. $this->_contents .= $data;
  1496. return $this;
  1497. }
  1498. /**
  1499. * Rotate the page.
  1500. *
  1501. * @param float $x - the X co-ordinate of rotation point
  1502. * @param float $y - the Y co-ordinate of rotation point
  1503. * @param float $angle - rotation angle
  1504. * @return Zend_Pdf_Page
  1505. */
  1506. public function rotate($x, $y, $angle)
  1507. {
  1508. $cos = new Zend_Pdf_Element_Numeric(cos($angle));
  1509. $sin = new Zend_Pdf_Element_Numeric(sin($angle));
  1510. $mSin = new Zend_Pdf_Element_Numeric(-$sin->value);
  1511. $xObj = new Zend_Pdf_Element_Numeric($x);
  1512. $yObj = new Zend_Pdf_Element_Numeric($y);
  1513. $mXObj = new Zend_Pdf_Element_Numeric(-$x);
  1514. $mYObj = new Zend_Pdf_Element_Numeric(-$y);
  1515. $this->_addProcSet('PDF');
  1516. $this->_contents .= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
  1517. . $cos->toString() . ' ' . $sin->toString() . ' ' . $mSin->toString() . ' ' . $cos->toString() . " 0 0 cm\n"
  1518. . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";
  1519. return $this;
  1520. }
  1521. /**
  1522. * Scale coordination system.
  1523. *
  1524. * @param float $xScale - X dimention scale factor
  1525. * @param float $yScale - Y dimention scale factor
  1526. * @return Zend_Pdf_Page
  1527. */
  1528. public function scale($xScale, $yScale)
  1529. {
  1530. $xScaleObj = new Zend_Pdf_Element_Numeric($xScale);
  1531. $yScaleObj = new Zend_Pdf_Element_Numeric($yScale);
  1532. $this->_addProcSet('PDF');
  1533. $this->_contents .= $xScaleObj->toString() . ' 0 0 ' . $yScaleObj->toString() . " 0 0 cm\n";
  1534. return $this;
  1535. }
  1536. /**
  1537. * Translate coordination system.
  1538. *
  1539. * @param float $xShift - X coordinate shift
  1540. * @param float $yShift - Y coordinate shift
  1541. * @return Zend_Pdf_Page
  1542. */
  1543. public function translate($xShift, $yShift)
  1544. {
  1545. $xShiftObj = new Zend_Pdf_Element_Numeric($xShift);
  1546. $yShiftObj = new Zend_Pdf_Element_Numeric($yShift);
  1547. $this->_addProcSet('PDF');
  1548. $this->_contents .= '1 0 0 1 ' . $xShiftObj->toString() . ' ' . $yShiftObj->toString() . " cm\n";
  1549. return $this;
  1550. }
  1551. /**
  1552. * Translate coordination system.
  1553. *
  1554. * @param float $x - the X co-ordinate of axis skew point
  1555. * @param float $y - the Y co-ordinate of axis skew point
  1556. * @param float $xAngle - X axis skew angle
  1557. * @param float $yAngle - Y axis skew angle
  1558. * @return Zend_Pdf_Page
  1559. */
  1560. public function skew($x, $y, $xAngle, $yAngle)
  1561. {
  1562. $tanXObj = new Zend_Pdf_Element_Numeric(tan($xAngle));
  1563. $tanYObj = new Zend_Pdf_Element_Numeric(-tan($yAngle));
  1564. $xObj = new Zend_Pdf_Element_Numeric($x);
  1565. $yObj = new Zend_Pdf_Element_Numeric($y);
  1566. $mXObj = new Zend_Pdf_Element_Numeric(-$x);
  1567. $mYObj = new Zend_Pdf_Element_Numeric(-$y);
  1568. $this->_addProcSet('PDF');
  1569. $this->_contents .= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
  1570. . '1 ' . $tanXObj->toString() . ' ' . $tanYObj->toString() . " 1 0 0 cm\n"
  1571. . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";
  1572. return $this;
  1573. }
  1574. }