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.

1217 lines
40 KiB

11 years ago
10 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: Style.php 20096 2010-01-06 02:05:09Z bkarwin $
  20. */
  21. // require_once 'Zend/Pdf/Canvas/Interface.php';
  22. /** Internally used classes */
  23. // require_once 'Zend/Pdf/Element.php';
  24. // require_once 'Zend/Pdf/Element/Array.php';
  25. // require_once 'Zend/Pdf/Element/String/Binary.php';
  26. // require_once 'Zend/Pdf/Element/Boolean.php';
  27. // require_once 'Zend/Pdf/Element/Dictionary.php';
  28. // require_once 'Zend/Pdf/Element/Name.php';
  29. // require_once 'Zend/Pdf/Element/Null.php';
  30. // require_once 'Zend/Pdf/Element/Numeric.php';
  31. // require_once 'Zend/Pdf/Element/String.php';
  32. // require_once 'Zend/Pdf/Resource/GraphicsState.php';
  33. // require_once 'Zend/Pdf/Resource/Font.php';
  34. // require_once 'Zend/Pdf/Resource/Image.php';
  35. /**
  36. * Canvas is an abstract rectangle drawing area which can be dropped into
  37. * page object at specified place.
  38. *
  39. * @package Zend_Pdf
  40. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  41. * @license http://framework.zend.com/license/new-bsd New BSD License
  42. */
  43. abstract class Zend_Pdf_Canvas_Abstract implements Zend_Pdf_Canvas_Interface
  44. {
  45. /**
  46. * Drawing instructions
  47. *
  48. * @var string
  49. */
  50. protected $_contents = '';
  51. /**
  52. * Current font
  53. *
  54. * @var Zend_Pdf_Resource_Font
  55. */
  56. protected $_font = null;
  57. /**
  58. * Current font size
  59. *
  60. * @var float
  61. */
  62. protected $_fontSize;
  63. /**
  64. * Current style
  65. *
  66. * @var Zend_Pdf_Style
  67. */
  68. protected $_style = null;
  69. /**
  70. * Counter for the "Save" operations
  71. *
  72. * @var integer
  73. */
  74. protected $_saveCount = 0;
  75. /**
  76. * Add procedureSet to the Page description
  77. *
  78. * @param string $procSetName
  79. */
  80. abstract protected function _addProcSet($procSetName);
  81. /**
  82. * Attach resource to the canvas
  83. *
  84. * Method returns a name of the resource which can be used
  85. * as a resource reference within drawing instructions stream
  86. * Allowed types: 'ExtGState', 'ColorSpace', 'Pattern', 'Shading',
  87. * 'XObject', 'Font', 'Properties'
  88. *
  89. * @param string $type
  90. * @param Zend_Pdf_Resource $resource
  91. * @return string
  92. */
  93. abstract protected function _attachResource($type, Zend_Pdf_Resource $resource);
  94. /**
  95. * Draw a canvas at the specified location
  96. *
  97. * If upper right corner is not specified then canvas heght and width
  98. * are used.
  99. *
  100. * @param Zend_Pdf_Canvas_Interface $canvas
  101. * @param float $x1
  102. * @param float $y1
  103. * @param float $x2
  104. * @param float $y2
  105. * @return Zend_Pdf_Canvas_Interface
  106. */
  107. public function drawCanvas(Zend_Pdf_Canvas_Interface $canvas, $x1, $y1, $x2 = null, $y2 = null)
  108. {
  109. $this->saveGS();
  110. $this->translate($x1, $y1);
  111. if ($x2 === null) {
  112. $with = $canvas->getWidth();
  113. } else {
  114. $with = $x2 - $x1;
  115. }
  116. if ($y2 === null) {
  117. $height = $canvas->getHeight();
  118. } else {
  119. $height = $y2 - $y1;
  120. }
  121. $this->clipRectangle(0, 0, $with, $height);
  122. if ($x2 !== null || $y2 !== null) {
  123. // Drawn canvas has to be scaled.
  124. if ($x2 !== null) {
  125. $xScale = $with/$canvas->getWidth();
  126. } else {
  127. $xScale = 1;
  128. }
  129. if ($y2 !== null) {
  130. $yScale = $height/$canvas->getHeight();
  131. } else {
  132. $yScale = 1;
  133. }
  134. $this->scale($xScale, $yScale);
  135. }
  136. $contentsToDraw = $canvas->getContents();
  137. /** @todo implementation */
  138. $this->restoreGS();
  139. return $this;
  140. }
  141. /**
  142. * Set fill color.
  143. *
  144. * @param Zend_Pdf_Color $color
  145. * @return Zend_Pdf_Canvas_Interface
  146. */
  147. public function setFillColor(Zend_Pdf_Color $color)
  148. {
  149. $this->_addProcSet('PDF');
  150. $this->_contents .= $color->instructions(false);
  151. return $this;
  152. }
  153. /**
  154. * Set line color.
  155. *
  156. * @param Zend_Pdf_Color $color
  157. * @return Zend_Pdf_Canvas_Interface
  158. */
  159. public function setLineColor(Zend_Pdf_Color $color)
  160. {
  161. $this->_addProcSet('PDF');
  162. $this->_contents .= $color->instructions(true);
  163. return $this;
  164. }
  165. /**
  166. * Set line width.
  167. *
  168. * @param float $width
  169. * @return Zend_Pdf_Canvas_Interface
  170. */
  171. public function setLineWidth($width)
  172. {
  173. $this->_addProcSet('PDF');
  174. $widthObj = new Zend_Pdf_Element_Numeric($width);
  175. $this->_contents .= $widthObj->toString() . " w\n";
  176. return $this;
  177. }
  178. /**
  179. * Set line dashing pattern
  180. *
  181. * Pattern is an array of floats: array(on_length, off_length, on_length, off_length, ...)
  182. * or Zend_Pdf_Page::LINE_DASHING_SOLID constant
  183. * Phase is shift from the beginning of line.
  184. *
  185. * @param mixed $pattern
  186. * @param array $phase
  187. * @return Zend_Pdf_Canvas_Interface
  188. */
  189. public function setLineDashingPattern($pattern, $phase = 0)
  190. {
  191. $this->_addProcSet('PDF');
  192. // require_once 'Zend/Pdf/Page.php';
  193. if ($pattern === Zend_Pdf_Page::LINE_DASHING_SOLID) {
  194. $pattern = array();
  195. $phase = 0;
  196. }
  197. $dashPattern = new Zend_Pdf_Element_Array();
  198. $phaseEleemnt = new Zend_Pdf_Element_Numeric($phase);
  199. foreach ($pattern as $dashItem) {
  200. $dashElement = new Zend_Pdf_Element_Numeric($dashItem);
  201. $dashPattern->items[] = $dashElement;
  202. }
  203. $this->_contents .= $dashPattern->toString() . ' '
  204. . $phaseEleemnt->toString() . " d\n";
  205. return $this;
  206. }
  207. /**
  208. * Set current font.
  209. *
  210. * @param Zend_Pdf_Resource_Font $font
  211. * @param float $fontSize
  212. * @return Zend_Pdf_Canvas_Interface
  213. */
  214. public function setFont(Zend_Pdf_Resource_Font $font, $fontSize)
  215. {
  216. $this->_addProcSet('Text');
  217. $fontName = $this->_attachResource('Font', $font);
  218. $this->_font = $font;
  219. $this->_fontSize = $fontSize;
  220. $fontNameObj = new Zend_Pdf_Element_Name($fontName);
  221. $fontSizeObj = new Zend_Pdf_Element_Numeric($fontSize);
  222. $this->_contents .= $fontNameObj->toString() . ' ' . $fontSizeObj->toString() . " Tf\n";
  223. return $this;
  224. }
  225. /**
  226. * Set the style to use for future drawing operations on this page
  227. *
  228. * @param Zend_Pdf_Style $style
  229. * @return Zend_Pdf_Canvas_Interface
  230. */
  231. public function setStyle(Zend_Pdf_Style $style)
  232. {
  233. $this->_addProcSet('Text');
  234. $this->_addProcSet('PDF');
  235. if ($style->getFont() !== null) {
  236. $this->setFont($style->getFont(), $style->getFontSize());
  237. }
  238. $this->_contents .= $style->instructions($this->_dictionary->Resources);
  239. $this->_style = $style;
  240. return $this;
  241. }
  242. /**
  243. * Get current font.
  244. *
  245. * @return Zend_Pdf_Resource_Font $font
  246. */
  247. public function getFont()
  248. {
  249. return $this->_font;
  250. }
  251. /**
  252. * Get current font size
  253. *
  254. * @return float $fontSize
  255. */
  256. public function getFontSize()
  257. {
  258. return $this->_fontSize;
  259. }
  260. /**
  261. * Return the style, applied to the page.
  262. *
  263. * @return Zend_Pdf_Style
  264. */
  265. public function getStyle()
  266. {
  267. return $this->_style;
  268. }
  269. /**
  270. * Save the graphics state of this page.
  271. * This takes a snapshot of the currently applied style, position, clipping area and
  272. * any rotation/translation/scaling that has been applied.
  273. *
  274. * @todo check for the open paths
  275. * @throws Zend_Pdf_Exception - if a save is performed with an open path
  276. * @return Zend_Pdf_Canvas_Interface
  277. */
  278. public function saveGS()
  279. {
  280. $this->_saveCount++;
  281. $this->_addProcSet('PDF');
  282. $this->_contents .= " q\n";
  283. return $this;
  284. }
  285. /**
  286. * Restore the graphics state that was saved with the last call to saveGS().
  287. *
  288. * @throws Zend_Pdf_Exception - if there is no previously saved state
  289. * @return Zend_Pdf_Canvas_Interface
  290. */
  291. public function restoreGS()
  292. {
  293. if ($this->_saveCount-- <= 0) {
  294. // require_once 'Zend/Pdf/Exception.php';
  295. throw new Zend_Pdf_Exception('Restoring graphics state which is not saved');
  296. }
  297. $this->_contents .= " Q\n";
  298. return $this;
  299. }
  300. /**
  301. * Set the transparancy
  302. *
  303. * $alpha == 0 - transparent
  304. * $alpha == 1 - opaque
  305. *
  306. * Transparency modes, supported by PDF:
  307. * Normal (default), Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight,
  308. * SoftLight, Difference, Exclusion
  309. *
  310. * @param float $alpha
  311. * @param string $mode
  312. * @return Zend_Pdf_Canvas_Interface
  313. */
  314. public function setAlpha($alpha, $mode = 'Normal')
  315. {
  316. $this->_addProcSet('Text');
  317. $this->_addProcSet('PDF');
  318. $graphicsState = new Zend_Pdf_Resource_GraphicsState();
  319. $graphicsState->setAlpha($alpha, $mode);
  320. $gStateName = $this->_attachResource('ExtGState', $graphicsState);
  321. $gStateNameObject = new Zend_Pdf_Element_Name($gStateName);
  322. $this->_contents .= $gStateNameObject->toString() . " gs\n";
  323. return $this;
  324. }
  325. /**
  326. * Intersect current clipping area with a circle.
  327. *
  328. * @param float $x
  329. * @param float $y
  330. * @param float $radius
  331. * @param float $startAngle
  332. * @param float $endAngle
  333. * @return Zend_Pdf_Canvas_Interface
  334. */
  335. public function clipCircle($x, $y, $radius, $startAngle = null, $endAngle = null)
  336. {
  337. $this->clipEllipse($x - $radius, $y - $radius,
  338. $x + $radius, $y + $radius,
  339. $startAngle, $endAngle);
  340. return $this;
  341. }
  342. /**
  343. * Intersect current clipping area with a polygon.
  344. *
  345. * Method signatures:
  346. * drawEllipse($x1, $y1, $x2, $y2);
  347. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  348. *
  349. * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
  350. *
  351. * @param float $x1
  352. * @param float $y1
  353. * @param float $x2
  354. * @param float $y2
  355. * @param float $startAngle
  356. * @param float $endAngle
  357. * @return Zend_Pdf_Canvas_Interface
  358. */
  359. public function clipEllipse($x1, $y1, $x2, $y2, $startAngle = null, $endAngle = null)
  360. {
  361. $this->_addProcSet('PDF');
  362. if ($x2 < $x1) {
  363. $temp = $x1;
  364. $x1 = $x2;
  365. $x2 = $temp;
  366. }
  367. if ($y2 < $y1) {
  368. $temp = $y1;
  369. $y1 = $y2;
  370. $y2 = $temp;
  371. }
  372. $x = ($x1 + $x2)/2.;
  373. $y = ($y1 + $y2)/2.;
  374. $xC = new Zend_Pdf_Element_Numeric($x);
  375. $yC = new Zend_Pdf_Element_Numeric($y);
  376. if ($startAngle !== null) {
  377. if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI*2); }
  378. if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI*2); }
  379. if ($startAngle > $endAngle) {
  380. $endAngle += M_PI*2;
  381. }
  382. $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
  383. $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4);
  384. $clipRadius = max($x2 - $x1, $y2 - $y1);
  385. for($count = 0; $count <= $clipSectors; $count++) {
  386. $pAngle = $startAngle + ($endAngle - $startAngle)*$count/(float)$clipSectors;
  387. $pX = new Zend_Pdf_Element_Numeric($x + cos($pAngle)*$clipRadius);
  388. $pY = new Zend_Pdf_Element_Numeric($y + sin($pAngle)*$clipRadius);
  389. $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
  390. }
  391. $this->_contents .= $clipPath . "h\nW\nn\n";
  392. }
  393. $xLeft = new Zend_Pdf_Element_Numeric($x1);
  394. $xRight = new Zend_Pdf_Element_Numeric($x2);
  395. $yUp = new Zend_Pdf_Element_Numeric($y2);
  396. $yDown = new Zend_Pdf_Element_Numeric($y1);
  397. $xDelta = 2*(M_SQRT2 - 1)*($x2 - $x1)/3.;
  398. $yDelta = 2*(M_SQRT2 - 1)*($y2 - $y1)/3.;
  399. $xr = new Zend_Pdf_Element_Numeric($x + $xDelta);
  400. $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
  401. $yu = new Zend_Pdf_Element_Numeric($y + $yDelta);
  402. $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
  403. $this->_contents .= $xC->toString() . ' ' . $yUp->toString() . " m\n"
  404. . $xr->toString() . ' ' . $yUp->toString() . ' '
  405. . $xRight->toString() . ' ' . $yu->toString() . ' '
  406. . $xRight->toString() . ' ' . $yC->toString() . " c\n"
  407. . $xRight->toString() . ' ' . $yd->toString() . ' '
  408. . $xr->toString() . ' ' . $yDown->toString() . ' '
  409. . $xC->toString() . ' ' . $yDown->toString() . " c\n"
  410. . $xl->toString() . ' ' . $yDown->toString() . ' '
  411. . $xLeft->toString() . ' ' . $yd->toString() . ' '
  412. . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
  413. . $xLeft->toString() . ' ' . $yu->toString() . ' '
  414. . $xl->toString() . ' ' . $yUp->toString() . ' '
  415. . $xC->toString() . ' ' . $yUp->toString() . " c\n"
  416. . "h\nW\nn\n";
  417. return $this;
  418. }
  419. /**
  420. * Intersect current clipping area with a polygon.
  421. *
  422. * @param array $x - array of float (the X co-ordinates of the vertices)
  423. * @param array $y - array of float (the Y co-ordinates of the vertices)
  424. * @param integer $fillMethod
  425. * @return Zend_Pdf_Canvas_Interface
  426. */
  427. public function clipPolygon($x, $y, $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING)
  428. {
  429. $this->_addProcSet('PDF');
  430. $firstPoint = true;
  431. foreach ($x as $id => $xVal) {
  432. $xObj = new Zend_Pdf_Element_Numeric($xVal);
  433. $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
  434. if ($firstPoint) {
  435. $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
  436. $firstPoint = false;
  437. } else {
  438. $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
  439. }
  440. }
  441. $this->_contents .= $path;
  442. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  443. $this->_contents .= " h\n W\nn\n";
  444. } else {
  445. // Even-Odd fill method.
  446. $this->_contents .= " h\n W*\nn\n";
  447. }
  448. return $this;
  449. }
  450. /**
  451. * Intersect current clipping area with a rectangle.
  452. *
  453. * @param float $x1
  454. * @param float $y1
  455. * @param float $x2
  456. * @param float $y2
  457. * @return Zend_Pdf_Canvas_Interface
  458. */
  459. public function clipRectangle($x1, $y1, $x2, $y2)
  460. {
  461. $this->_addProcSet('PDF');
  462. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  463. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  464. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  465. $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  466. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  467. . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n"
  468. . " W\nn\n";
  469. return $this;
  470. }
  471. // ------------------------------------------------------------------------------------------
  472. /**
  473. * Draw a circle centered on x, y with a radius of radius.
  474. *
  475. * Method signatures:
  476. * drawCircle($x, $y, $radius);
  477. * drawCircle($x, $y, $radius, $fillType);
  478. * drawCircle($x, $y, $radius, $startAngle, $endAngle);
  479. * drawCircle($x, $y, $radius, $startAngle, $endAngle, $fillType);
  480. *
  481. *
  482. * It's not a really circle, because PDF supports only cubic Bezier curves.
  483. * But _very_ good approximation.
  484. * It differs from a real circle on a maximum 0.00026 radiuses
  485. * (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).
  486. * 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.
  487. *
  488. * @param float $x
  489. * @param float $y
  490. * @param float $radius
  491. * @param mixed $param4
  492. * @param mixed $param5
  493. * @param mixed $param6
  494. * @return Zend_Pdf_Canvas_Interface
  495. */
  496. public function drawCircle($x, $y, $radius, $param4 = null, $param5 = null, $param6 = null)
  497. {
  498. $this->drawEllipse($x - $radius, $y - $radius,
  499. $x + $radius, $y + $radius,
  500. $param4, $param5, $param6);
  501. return $this;
  502. }
  503. /**
  504. * Draw an ellipse inside the specified rectangle.
  505. *
  506. * Method signatures:
  507. * drawEllipse($x1, $y1, $x2, $y2);
  508. * drawEllipse($x1, $y1, $x2, $y2, $fillType);
  509. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  510. * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
  511. *
  512. * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
  513. *
  514. * @param float $x1
  515. * @param float $y1
  516. * @param float $x2
  517. * @param float $y2
  518. * @param mixed $param5
  519. * @param mixed $param6
  520. * @param mixed $param7
  521. * @return Zend_Pdf_Canvas_Interface
  522. */
  523. public function drawEllipse($x1, $y1, $x2, $y2, $param5 = null, $param6 = null, $param7 = null)
  524. {
  525. if ($param5 === null) {
  526. // drawEllipse($x1, $y1, $x2, $y2);
  527. $startAngle = null;
  528. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE;
  529. } else if ($param6 === null) {
  530. // drawEllipse($x1, $y1, $x2, $y2, $fillType);
  531. $startAngle = null;
  532. $fillType = $param5;
  533. } else {
  534. // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
  535. // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
  536. $startAngle = $param5;
  537. $endAngle = $param6;
  538. if ($param7 === null) {
  539. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE;
  540. } else {
  541. $fillType = $param7;
  542. }
  543. }
  544. $this->_addProcSet('PDF');
  545. if ($x2 < $x1) {
  546. $temp = $x1;
  547. $x1 = $x2;
  548. $x2 = $temp;
  549. }
  550. if ($y2 < $y1) {
  551. $temp = $y1;
  552. $y1 = $y2;
  553. $y2 = $temp;
  554. }
  555. $x = ($x1 + $x2)/2.;
  556. $y = ($y1 + $y2)/2.;
  557. $xC = new Zend_Pdf_Element_Numeric($x);
  558. $yC = new Zend_Pdf_Element_Numeric($y);
  559. if ($startAngle !== null) {
  560. if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI*2); }
  561. if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI*2); }
  562. if ($startAngle > $endAngle) {
  563. $endAngle += M_PI*2;
  564. }
  565. $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
  566. $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4);
  567. $clipRadius = max($x2 - $x1, $y2 - $y1);
  568. for($count = 0; $count <= $clipSectors; $count++) {
  569. $pAngle = $startAngle + ($endAngle - $startAngle)*$count/(float)$clipSectors;
  570. $pX = new Zend_Pdf_Element_Numeric($x + cos($pAngle)*$clipRadius);
  571. $pY = new Zend_Pdf_Element_Numeric($y + sin($pAngle)*$clipRadius);
  572. $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
  573. }
  574. $this->_contents .= "q\n" . $clipPath . "h\nW\nn\n";
  575. }
  576. $xLeft = new Zend_Pdf_Element_Numeric($x1);
  577. $xRight = new Zend_Pdf_Element_Numeric($x2);
  578. $yUp = new Zend_Pdf_Element_Numeric($y2);
  579. $yDown = new Zend_Pdf_Element_Numeric($y1);
  580. $xDelta = 2*(M_SQRT2 - 1)*($x2 - $x1)/3.;
  581. $yDelta = 2*(M_SQRT2 - 1)*($y2 - $y1)/3.;
  582. $xr = new Zend_Pdf_Element_Numeric($x + $xDelta);
  583. $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
  584. $yu = new Zend_Pdf_Element_Numeric($y + $yDelta);
  585. $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
  586. $this->_contents .= $xC->toString() . ' ' . $yUp->toString() . " m\n"
  587. . $xr->toString() . ' ' . $yUp->toString() . ' '
  588. . $xRight->toString() . ' ' . $yu->toString() . ' '
  589. . $xRight->toString() . ' ' . $yC->toString() . " c\n"
  590. . $xRight->toString() . ' ' . $yd->toString() . ' '
  591. . $xr->toString() . ' ' . $yDown->toString() . ' '
  592. . $xC->toString() . ' ' . $yDown->toString() . " c\n"
  593. . $xl->toString() . ' ' . $yDown->toString() . ' '
  594. . $xLeft->toString() . ' ' . $yd->toString() . ' '
  595. . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
  596. . $xLeft->toString() . ' ' . $yu->toString() . ' '
  597. . $xl->toString() . ' ' . $yUp->toString() . ' '
  598. . $xC->toString() . ' ' . $yUp->toString() . " c\n";
  599. switch ($fillType) {
  600. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  601. $this->_contents .= " B*\n";
  602. break;
  603. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  604. $this->_contents .= " f*\n";
  605. break;
  606. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  607. $this->_contents .= " S\n";
  608. break;
  609. }
  610. if ($startAngle !== null) {
  611. $this->_contents .= "Q\n";
  612. }
  613. return $this;
  614. }
  615. /**
  616. * Draw an image at the specified position on the page.
  617. *
  618. * @param Zend_Pdf_Image $image
  619. * @param float $x1
  620. * @param float $y1
  621. * @param float $x2
  622. * @param float $y2
  623. * @return Zend_Pdf_Canvas_Interface
  624. */
  625. public function drawImage(Zend_Pdf_Resource_Image $image, $x1, $y1, $x2, $y2)
  626. {
  627. $this->_addProcSet('PDF');
  628. $imageName = $this->_attachResource('XObject', $image);
  629. $imageNameObj = new Zend_Pdf_Element_Name($imageName);
  630. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  631. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  632. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  633. $heightObj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  634. $this->_contents .= "q\n"
  635. . '1 0 0 1 ' . $x1Obj->toString() . ' ' . $y1Obj->toString() . " cm\n"
  636. . $widthObj->toString() . ' 0 0 ' . $heightObj->toString() . " 0 0 cm\n"
  637. . $imageNameObj->toString() . " Do\n"
  638. . "Q\n";
  639. return $this;
  640. }
  641. /**
  642. * Draw a LayoutBox at the specified position on the page.
  643. *
  644. * @internal (not implemented now)
  645. *
  646. * @param Zend_Pdf_Element_LayoutBox $box
  647. * @param float $x
  648. * @param float $y
  649. * @return Zend_Pdf_Canvas_Interface
  650. */
  651. public function drawLayoutBox($box, $x, $y)
  652. {
  653. /** @todo implementation */
  654. return $this;
  655. }
  656. /**
  657. * Draw a line from x1,y1 to x2,y2.
  658. *
  659. * @param float $x1
  660. * @param float $y1
  661. * @param float $x2
  662. * @param float $y2
  663. * @return Zend_Pdf_Canvas_Interface
  664. */
  665. public function drawLine($x1, $y1, $x2, $y2)
  666. {
  667. $this->_addProcSet('PDF');
  668. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  669. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  670. $x2Obj = new Zend_Pdf_Element_Numeric($x2);
  671. $y2Obj = new Zend_Pdf_Element_Numeric($y2);
  672. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " m\n"
  673. . $x2Obj->toString() . ' ' . $y2Obj->toString() . " l\n S\n";
  674. return $this;
  675. }
  676. /**
  677. * Draw a polygon.
  678. *
  679. * If $fillType is Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE or
  680. * Zend_Pdf_Page::SHAPE_DRAW_FILL, then polygon is automatically closed.
  681. * See detailed description of these methods in a PDF documentation
  682. * (section 4.4.2 Path painting Operators, Filling)
  683. *
  684. * @param array $x - array of float (the X co-ordinates of the vertices)
  685. * @param array $y - array of float (the Y co-ordinates of the vertices)
  686. * @param integer $fillType
  687. * @param integer $fillMethod
  688. * @return Zend_Pdf_Canvas_Interface
  689. */
  690. public function drawPolygon($x, $y,
  691. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE,
  692. $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING)
  693. {
  694. $this->_addProcSet('PDF');
  695. $firstPoint = true;
  696. foreach ($x as $id => $xVal) {
  697. $xObj = new Zend_Pdf_Element_Numeric($xVal);
  698. $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
  699. if ($firstPoint) {
  700. $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
  701. $firstPoint = false;
  702. } else {
  703. $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
  704. }
  705. }
  706. $this->_contents .= $path;
  707. switch ($fillType) {
  708. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  709. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  710. $this->_contents .= " b\n";
  711. } else {
  712. // Even-Odd fill method.
  713. $this->_contents .= " b*\n";
  714. }
  715. break;
  716. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  717. if ($fillMethod == Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING) {
  718. $this->_contents .= " h\n f\n";
  719. } else {
  720. // Even-Odd fill method.
  721. $this->_contents .= " h\n f*\n";
  722. }
  723. break;
  724. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  725. $this->_contents .= " S\n";
  726. break;
  727. }
  728. return $this;
  729. }
  730. /**
  731. * Draw a rectangle.
  732. *
  733. * Fill types:
  734. * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
  735. * Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
  736. * Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
  737. *
  738. * @param float $x1
  739. * @param float $y1
  740. * @param float $x2
  741. * @param float $y2
  742. * @param integer $fillType
  743. * @return Zend_Pdf_Canvas_Interface
  744. */
  745. public function drawRectangle($x1, $y1, $x2, $y2, $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE)
  746. {
  747. $this->_addProcSet('PDF');
  748. $x1Obj = new Zend_Pdf_Element_Numeric($x1);
  749. $y1Obj = new Zend_Pdf_Element_Numeric($y1);
  750. $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
  751. $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
  752. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  753. . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n";
  754. switch ($fillType) {
  755. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  756. $this->_contents .= " B*\n";
  757. break;
  758. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  759. $this->_contents .= " f*\n";
  760. break;
  761. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  762. $this->_contents .= " S\n";
  763. break;
  764. }
  765. return $this;
  766. }
  767. /**
  768. * Draw a rounded rectangle.
  769. *
  770. * Fill types:
  771. * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
  772. * Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
  773. * Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
  774. *
  775. * radius is an integer representing radius of the four corners, or an array
  776. * of four integers representing the radius starting at top left, going
  777. * clockwise
  778. *
  779. * @param float $x1
  780. * @param float $y1
  781. * @param float $x2
  782. * @param float $y2
  783. * @param integer|array $radius
  784. * @param integer $fillType
  785. * @return Zend_Pdf_Canvas_Interface
  786. */
  787. public function drawRoundedRectangle($x1, $y1, $x2, $y2, $radius,
  788. $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE)
  789. {
  790. $this->_addProcSet('PDF');
  791. if(!is_array($radius)) {
  792. $radius = array($radius, $radius, $radius, $radius);
  793. } else {
  794. for ($i = 0; $i < 4; $i++) {
  795. if(!isset($radius[$i])) {
  796. $radius[$i] = 0;
  797. }
  798. }
  799. }
  800. $topLeftX = $x1;
  801. $topLeftY = $y2;
  802. $topRightX = $x2;
  803. $topRightY = $y2;
  804. $bottomRightX = $x2;
  805. $bottomRightY = $y1;
  806. $bottomLeftX = $x1;
  807. $bottomLeftY = $y1;
  808. //draw top side
  809. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX + $radius[0]);
  810. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  811. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " m\n";
  812. $x1Obj = new Zend_Pdf_Element_Numeric($topRightX - $radius[1]);
  813. $y1Obj = new Zend_Pdf_Element_Numeric($topRightY);
  814. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  815. //draw top right corner if needed
  816. if ($radius[1] != 0) {
  817. $x1Obj = new Zend_Pdf_Element_Numeric($topRightX);
  818. $y1Obj = new Zend_Pdf_Element_Numeric($topRightY);
  819. $x2Obj = new Zend_Pdf_Element_Numeric($topRightX);
  820. $y2Obj = new Zend_Pdf_Element_Numeric($topRightY);
  821. $x3Obj = new Zend_Pdf_Element_Numeric($topRightX);
  822. $y3Obj = new Zend_Pdf_Element_Numeric($topRightY - $radius[1]);
  823. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  824. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  825. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  826. . " c\n";
  827. }
  828. //draw right side
  829. $x1Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  830. $y1Obj = new Zend_Pdf_Element_Numeric($bottomRightY + $radius[2]);
  831. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  832. //draw bottom right corner if needed
  833. if ($radius[2] != 0) {
  834. $x1Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  835. $y1Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  836. $x2Obj = new Zend_Pdf_Element_Numeric($bottomRightX);
  837. $y2Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  838. $x3Obj = new Zend_Pdf_Element_Numeric($bottomRightX - $radius[2]);
  839. $y3Obj = new Zend_Pdf_Element_Numeric($bottomRightY);
  840. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  841. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  842. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  843. . " c\n";
  844. }
  845. //draw bottom side
  846. $x1Obj = new Zend_Pdf_Element_Numeric($bottomLeftX + $radius[3]);
  847. $y1Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  848. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  849. //draw bottom left corner if needed
  850. if ($radius[3] != 0) {
  851. $x1Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  852. $y1Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  853. $x2Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  854. $y2Obj = new Zend_Pdf_Element_Numeric($bottomLeftY);
  855. $x3Obj = new Zend_Pdf_Element_Numeric($bottomLeftX);
  856. $y3Obj = new Zend_Pdf_Element_Numeric($bottomLeftY + $radius[3]);
  857. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  858. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  859. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  860. . " c\n";
  861. }
  862. //draw left side
  863. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  864. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY - $radius[0]);
  865. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . " l\n";
  866. //draw top left corner if needed
  867. if ($radius[0] != 0) {
  868. $x1Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  869. $y1Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  870. $x2Obj = new Zend_Pdf_Element_Numeric($topLeftX);
  871. $y2Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  872. $x3Obj = new Zend_Pdf_Element_Numeric($topLeftX + $radius[0]);
  873. $y3Obj = new Zend_Pdf_Element_Numeric($topLeftY);
  874. $this->_contents .= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
  875. . $x2Obj->toString() . ' ' . $y2Obj->toString() . ' '
  876. . $x3Obj->toString() . ' ' . $y3Obj->toString() . ' '
  877. . " c\n";
  878. }
  879. switch ($fillType) {
  880. case Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE:
  881. $this->_contents .= " B*\n";
  882. break;
  883. case Zend_Pdf_Page::SHAPE_DRAW_FILL:
  884. $this->_contents .= " f*\n";
  885. break;
  886. case Zend_Pdf_Page::SHAPE_DRAW_STROKE:
  887. $this->_contents .= " S\n";
  888. break;
  889. }
  890. return $this;
  891. }
  892. /**
  893. * Draw a line of text at the specified position.
  894. *
  895. * @param string $text
  896. * @param float $x
  897. * @param float $y
  898. * @param string $charEncoding (optional) Character encoding of source text.
  899. * Defaults to current locale.
  900. * @throws Zend_Pdf_Exception
  901. * @return Zend_Pdf_Canvas_Interface
  902. */
  903. public function drawText($text, $x, $y, $charEncoding = '')
  904. {
  905. if ($this->_font === null) {
  906. // require_once 'Zend/Pdf/Exception.php';
  907. throw new Zend_Pdf_Exception('Font has not been set');
  908. }
  909. $this->_addProcSet('Text');
  910. $textObj = new Zend_Pdf_Element_String($this->_font->encodeString($text, $charEncoding));
  911. $xObj = new Zend_Pdf_Element_Numeric($x);
  912. $yObj = new Zend_Pdf_Element_Numeric($y);
  913. $this->_contents .= "BT\n"
  914. . $xObj->toString() . ' ' . $yObj->toString() . " Td\n"
  915. . $textObj->toString() . " Tj\n"
  916. . "ET\n";
  917. return $this;
  918. }
  919. /**
  920. * Close the path by drawing a straight line back to it's beginning.
  921. *
  922. * @internal (needs implementation)
  923. *
  924. * @throws Zend_Pdf_Exception - if a path hasn't been started with pathMove()
  925. * @return Zend_Pdf_Canvas_Interface
  926. */
  927. public function pathClose()
  928. {
  929. /** @todo implementation */
  930. return $this;
  931. }
  932. /**
  933. * Continue the open path in a straight line to the specified position.
  934. *
  935. * @internal (needs implementation)
  936. *
  937. * @param float $x - the X co-ordinate to move to
  938. * @param float $y - the Y co-ordinate to move to
  939. * @return Zend_Pdf_Canvas_Interface
  940. */
  941. public function pathLine($x, $y)
  942. {
  943. /** @todo implementation */
  944. return $this;
  945. }
  946. /**
  947. * Start a new path at the specified position. If a path has already been started,
  948. * move the cursor without drawing a line.
  949. *
  950. * @internal (needs implementation)
  951. *
  952. * @param float $x - the X co-ordinate to move to
  953. * @param float $y - the Y co-ordinate to move to
  954. * @return Zend_Pdf_Canvas_Interface
  955. */
  956. public function pathMove($x, $y)
  957. {
  958. /** @todo implementation */
  959. return $this;
  960. }
  961. /**
  962. * Rotate the page.
  963. *
  964. * @param float $x - the X co-ordinate of rotation point
  965. * @param float $y - the Y co-ordinate of rotation point
  966. * @param float $angle - rotation angle
  967. * @return Zend_Pdf_Canvas_Interface
  968. */
  969. public function rotate($x, $y, $angle)
  970. {
  971. $cos = new Zend_Pdf_Element_Numeric(cos($angle));
  972. $sin = new Zend_Pdf_Element_Numeric(sin($angle));
  973. $mSin = new Zend_Pdf_Element_Numeric(-$sin->value);
  974. $xObj = new Zend_Pdf_Element_Numeric($x);
  975. $yObj = new Zend_Pdf_Element_Numeric($y);
  976. $mXObj = new Zend_Pdf_Element_Numeric(-$x);
  977. $mYObj = new Zend_Pdf_Element_Numeric(-$y);
  978. $this->_addProcSet('PDF');
  979. $this->_contents .= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
  980. . $cos->toString() . ' ' . $sin->toString() . ' ' . $mSin->toString() . ' ' . $cos->toString() . " 0 0 cm\n"
  981. . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";
  982. return $this;
  983. }
  984. /**
  985. * Scale coordination system.
  986. *
  987. * @param float $xScale - X dimention scale factor
  988. * @param float $yScale - Y dimention scale factor
  989. * @return Zend_Pdf_Canvas_Interface
  990. */
  991. public function scale($xScale, $yScale)
  992. {
  993. $xScaleObj = new Zend_Pdf_Element_Numeric($xScale);
  994. $yScaleObj = new Zend_Pdf_Element_Numeric($yScale);
  995. $this->_addProcSet('PDF');
  996. $this->_contents .= $xScaleObj->toString() . ' 0 0 ' . $yScaleObj->toString() . " 0 0 cm\n";
  997. return $this;
  998. }
  999. /**
  1000. * Translate coordination system.
  1001. *
  1002. * @param float $xShift - X coordinate shift
  1003. * @param float $yShift - Y coordinate shift
  1004. * @return Zend_Pdf_Canvas_Interface
  1005. */
  1006. public function translate($xShift, $yShift)
  1007. {
  1008. $xShiftObj = new Zend_Pdf_Element_Numeric($xShift);
  1009. $yShiftObj = new Zend_Pdf_Element_Numeric($yShift);
  1010. $this->_addProcSet('PDF');
  1011. $this->_contents .= '1 0 0 1 ' . $xShiftObj->toString() . ' ' . $yShiftObj->toString() . " cm\n";
  1012. return $this;
  1013. }
  1014. /**
  1015. * Translate coordination system.
  1016. *
  1017. * @param float $x - the X co-ordinate of axis skew point
  1018. * @param float $y - the Y co-ordinate of axis skew point
  1019. * @param float $xAngle - X axis skew angle
  1020. * @param float $yAngle - Y axis skew angle
  1021. * @return Zend_Pdf_Canvas_Interface
  1022. */
  1023. public function skew($x, $y, $xAngle, $yAngle)
  1024. {
  1025. $tanXObj = new Zend_Pdf_Element_Numeric(tan($xAngle));
  1026. $tanYObj = new Zend_Pdf_Element_Numeric(-tan($yAngle));
  1027. $xObj = new Zend_Pdf_Element_Numeric($x);
  1028. $yObj = new Zend_Pdf_Element_Numeric($y);
  1029. $mXObj = new Zend_Pdf_Element_Numeric(-$x);
  1030. $mYObj = new Zend_Pdf_Element_Numeric(-$y);
  1031. $this->_addProcSet('PDF');
  1032. $this->_contents .= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
  1033. . '1 ' . $tanXObj->toString() . ' ' . $tanYObj->toString() . " 1 0 0 cm\n"
  1034. . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";
  1035. return $this;
  1036. }
  1037. /**
  1038. * Writes the raw data to the page's content stream.
  1039. *
  1040. * Be sure to consult the PDF reference to ensure your syntax is correct. No
  1041. * attempt is made to ensure the validity of the stream data.
  1042. *
  1043. * @param string $data
  1044. * @param string $procSet (optional) Name of ProcSet to add.
  1045. * @return Zend_Pdf_Canvas_Interface
  1046. */
  1047. public function rawWrite($data, $procSet = null)
  1048. {
  1049. if (! empty($procSet)) {
  1050. $this->_addProcSet($procSet);
  1051. }
  1052. $this->_contents .= $data;
  1053. return $this;
  1054. }
  1055. }