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.
16 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
17 * @license http://framework.zend.com/license/new-bsd New BSD License
20 /** Zend_Pdf_Resource_Font */
21 require_once 'Zend/Pdf/Resource/Font.php';
24 require_once 'Zend/Pdf/Style.php';
26 /** Zend_Pdf_Element_Dictionary */
27 require_once 'Zend/Pdf/Element/Dictionary.php';
29 /** Zend_Pdf_Element_Reference */
30 require_once 'Zend/Pdf/Element/Reference.php';
32 /** Zend_Pdf_ElementFactory */
33 require_once 'Zend/Pdf/ElementFactory.php';
36 require_once 'Zend/Pdf/Color.php';
38 /** Zend_Pdf_Color_GrayScale */
39 require_once 'Zend/Pdf/Color/GrayScale.php';
41 /** Zend_Pdf_Color_Rgb */
42 require_once 'Zend/Pdf/Color/Rgb.php';
44 /** Zend_Pdf_Color_Cmyk */
45 require_once 'Zend/Pdf/Color/Cmyk.php';
51 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
52 * @license http://framework.zend.com/license/new-bsd New BSD License
56 /**** Class Constants ****/
62 * Size representing an A4 page in portrait (tall) orientation.
64 const SIZE_A4
= '595:842:';
67 * Size representing an A4 page in landscape (wide) orientation.
69 const SIZE_A4_LANDSCAPE
= '842:595:';
72 * Size representing a US Letter page in portrait (tall) orientation.
74 const SIZE_LETTER
= '612:792:';
77 * Size representing a US Letter page in landscape (wide) orientation.
79 const SIZE_LETTER_LANDSCAPE
= '792:612:';
85 * Stroke the path only. Do not fill.
87 const SHAPE_DRAW_STROKE
= 0;
90 * Fill the path only. Do not stroke.
92 const SHAPE_DRAW_FILL
= 1;
95 * Fill and stroke the path.
97 const SHAPE_DRAW_FILL_AND_STROKE
= 2;
100 /* Shape Filling Methods */
103 * Fill the path using the non-zero winding rule.
105 const FILL_METHOD_NON_ZERO_WINDING
= 0;
108 * Fill the path using the even-odd rule.
110 const FILL_METHOD_EVEN_ODD
= 1;
113 /* Line Dash Types */
118 const LINE_DASHING_SOLID
= 0;
123 * Reference to the object with page dictionary.
125 * @var Zend_Pdf_Element_Reference
127 protected $_pageDictionary;
130 * PDF objects factory.
132 * @var Zend_Pdf_ElementFactory_Interface
134 protected $_objFactory = null;
137 * Flag which signals, that page is created separately from any PDF document or
138 * attached to anyone.
142 protected $_attached;
145 * Stream of the drawing instractions.
149 protected $_contents = '';
154 * @var Zend_Pdf_Style
156 protected $_style = null;
159 * Counter for the "Save" operations
163 protected $_saveCount = 0;
166 * Safe Graphics State semafore
168 * If it's false, than we can't be sure Graphics State is restored withing
169 * context of previous contents stream (ex. drawing coordinate system may be rotated).
170 * We should encompass existing content with save/restore GS operators
179 * @var Zend_Pdf_Resource_Font
181 protected $_font = null;
188 protected $_fontSize;
191 * Object constructor.
192 * Constructor signatures:
194 * 1. Load PDF page from a parsed PDF file.
195 * Object factory is created by PDF parser.
196 * ---------------------------------------------------------
197 * new Zend_Pdf_Page(Zend_Pdf_Element_Dictionary $pageDict,
198 * Zend_Pdf_ElementFactory_Interface $factory);
199 * ---------------------------------------------------------
202 * New page is created in the same context as source page. Object factory is shared.
203 * Thus it will be attached to the document, but need to be placed into Zend_Pdf::$pages array
204 * to be included into output.
205 * ---------------------------------------------------------
206 * new Zend_Pdf_Page(Zend_Pdf_Page $page);
207 * ---------------------------------------------------------
209 * 3. Create new page with a specified pagesize.
210 * If $factory is null then it will be created and page must be attached to the document to be
211 * included into output.
212 * ---------------------------------------------------------
213 * new Zend_Pdf_Page(string $pagesize, Zend_Pdf_ElementFactory_Interface $factory = null);
214 * ---------------------------------------------------------
216 * 4. Create new page with a specified pagesize (in default user space units).
217 * If $factory is null then it will be created and page must be attached to the document to be
218 * included into output.
219 * ---------------------------------------------------------
220 * new Zend_Pdf_Page(numeric $width, numeric $height, Zend_Pdf_ElementFactory_Interface $factory = null);
221 * ---------------------------------------------------------
224 * @param mixed $param1
225 * @param mixed $param2
226 * @param mixed $param3
227 * @throws Zend_Pdf_Exception
229 public function __construct($param1, $param2 = null, $param3 = null)
231 if ($param1 instanceof Zend_Pdf_Element_Reference
&&
232 $param1->getType() == Zend_Pdf_Element
::TYPE_DICTIONARY
&&
233 $param2 instanceof Zend_Pdf_ElementFactory_Interface
&&
236 $this->_pageDictionary
= $param1;
237 $this->_objFactory
= $param2;
238 $this->_attached
= true;
239 $this->_safeGS
= false;
243 } else if ($param1 instanceof Zend_Pdf_Page
&& $param2 === null && $param3 === null) {
244 // Clone existing page.
245 // Let already existing content and resources to be shared between pages
246 // We don't give existing content modification functionality, so we don't need "deep copy"
247 $this->_objFactory
= $param1->_objFactory
;
248 $this->_attached
= &$param1->_attached
;
249 $this->_safeGS
= false;
251 $this->_pageDictionary
= $this->_objFactory
->newObject(new Zend_Pdf_Element_Dictionary());
253 foreach ($param1->_pageDictionary
->getKeys() as $key) {
254 if ($key == 'Contents') {
255 // Clone Contents property
257 $this->_pageDictionary
->Contents
= new Zend_Pdf_Element_Array();
259 if ($param1->_pageDictionary
->Contents
->getType() != Zend_Pdf_Element
::TYPE_ARRAY
) {
260 // Prepare array of content streams and add existing stream
261 $this->_pageDictionary
->Contents
->items
[] = $param1->_pageDictionary
->Contents
;
263 // Clone array of the content streams
264 foreach ($param1->_pageDictionary
->Contents
->items
as $srcContentStream) {
265 $this->_pageDictionary
->Contents
->items
[] = $srcContentStream;
269 $this->_pageDictionary
->$key = $param1->_pageDictionary
->$key;
274 } else if (is_string($param1) &&
275 ($param2 === null ||
$param2 instanceof Zend_Pdf_ElementFactory_Interface
) &&
277 $this->_objFactory
= ($param2 !== null)?
$param2 : Zend_Pdf_ElementFactory
::createFactory(1);
278 $this->_attached
= false;
279 $this->_safeGS
= true; /** New page created. That's users App responsibility to track GS changes */
281 switch (strtolower($param1)) {
283 $param1 = Zend_Pdf_Page
::SIZE_A4
;
286 $param1 = Zend_Pdf_Page
::SIZE_A4_LANDSCAPE
;
289 $param1 = Zend_Pdf_Page
::SIZE_LETTER
;
291 case 'letter-landscape':
292 $param1 = Zend_Pdf_Page
::SIZE_LETTER_LANDSCAPE
;
295 // should be in "x:y" form
298 $pageDim = explode(':', $param1);
299 if(count($pageDim) == 3) {
300 $pageWidth = $pageDim[0];
301 $pageHeight = $pageDim[1];
304 * @todo support of user defined pagesize notations, like:
305 * "210x297mm", "595x842", "8.5x11in", "612x792"
307 require_once 'Zend/Pdf/Exception.php';
308 throw new Zend_Pdf_Exception('Wrong pagesize notation.');
311 * @todo support of pagesize recalculation to "default user space units"
314 } else if (is_numeric($param1) && is_numeric($param2) &&
315 ($param3 === null ||
$param3 instanceof Zend_Pdf_ElementFactory_Interface
)) {
316 $this->_objFactory
= ($param3 !== null)?
$param3 : Zend_Pdf_ElementFactory
::createFactory(1);
317 $this->_attached
= false;
318 $this->_safeGS
= true; /** New page created. That's users App responsibility to track GS changes */
319 $pageWidth = $param1;
320 $pageHeight = $param2;
323 require_once 'Zend/Pdf/Exception.php';
324 throw new Zend_Pdf_Exception('Unrecognized method signature, wrong number of arguments or wrong argument types.');
327 $this->_pageDictionary
= $this->_objFactory
->newObject(new Zend_Pdf_Element_Dictionary());
328 $this->_pageDictionary
->Type
= new Zend_Pdf_Element_Name('Page');
329 $this->_pageDictionary
->LastModified
= new Zend_Pdf_Element_String(Zend_Pdf
::pdfDate());
330 $this->_pageDictionary
->Resources
= new Zend_Pdf_Element_Dictionary();
331 $this->_pageDictionary
->MediaBox
= new Zend_Pdf_Element_Array();
332 $this->_pageDictionary
->MediaBox
->items
[] = new Zend_Pdf_Element_Numeric(0);
333 $this->_pageDictionary
->MediaBox
->items
[] = new Zend_Pdf_Element_Numeric(0);
334 $this->_pageDictionary
->MediaBox
->items
[] = new Zend_Pdf_Element_Numeric($pageWidth);
335 $this->_pageDictionary
->MediaBox
->items
[] = new Zend_Pdf_Element_Numeric($pageHeight);
336 $this->_pageDictionary
->Contents
= new Zend_Pdf_Element_Array();
343 * @throws Zend_Pdf_Exception
345 public function __clone()
347 require_once 'Zend/Pdf/Exception.php';
348 throw new Zend_Pdf_Exception('Cloning Zend_Pdf_Page object using \'clone\' keyword is not supported. Use \'new Zend_Pdf_Page($srcPage)\' syntax');
352 * Attach resource to the page
354 * @param string $type
355 * @param Zend_Pdf_Resource $resource
358 protected function _attachResource($type, Zend_Pdf_Resource
$resource)
360 // Check that Resources dictionary contains appropriate resource set
361 if ($this->_pageDictionary
->Resources
->$type === null) {
362 $this->_pageDictionary
->Resources
->touch();
363 $this->_pageDictionary
->Resources
->$type = new Zend_Pdf_Element_Dictionary();
365 $this->_pageDictionary
->Resources
->$type->touch();
368 // Check, that resource is already attached to resource set.
369 $resObject = $resource->getResource();
370 foreach ($this->_pageDictionary
->Resources
->$type->getKeys() as $ResID) {
371 if ($this->_pageDictionary
->Resources
->$type->$ResID === $resObject) {
378 $newResName = $type[0] . $idCounter++
;
379 } while ($this->_pageDictionary
->Resources
->$type->$newResName !== null);
381 $this->_pageDictionary
->Resources
->$type->$newResName = $resObject;
382 $this->_objFactory
->attach($resource->getFactory());
388 * Add procedureSet to the Page description
390 * @param string $procSetName
392 protected function _addProcSet($procSetName)
394 // Check that Resources dictionary contains ProcSet entry
395 if ($this->_pageDictionary
->Resources
->ProcSet
=== null) {
396 $this->_pageDictionary
->Resources
->touch();
397 $this->_pageDictionary
->Resources
->ProcSet
= new Zend_Pdf_Element_Array();
399 $this->_pageDictionary
->Resources
->ProcSet
->touch();
402 foreach ($this->_pageDictionary
->Resources
->ProcSet
->items
as $procSetEntry) {
403 if ($procSetEntry->value
== $procSetName) {
404 // Procset is already included into a ProcSet array
409 $this->_pageDictionary
->Resources
->ProcSet
->items
[] = new Zend_Pdf_Element_Name($procSetName);
413 * Retrive PDF file reference to the page
415 * @return Zend_Pdf_Element_Dictionary
417 public function getPageDictionary()
419 return $this->_pageDictionary
;
423 * Dump current drawing instructions into the content stream.
425 * @todo Don't forget to close all current graphics operations (like path drawing)
427 * @throws Zend_Pdf_Exception
429 public function flush()
431 if ($this->_saveCount
!= 0) {
432 require_once 'Zend/Pdf/Exception.php';
433 throw new Zend_Pdf_Exception('Saved graphics state is not restored');
436 if ($this->_contents
== '') {
440 if ($this->_pageDictionary
->Contents
->getType() != Zend_Pdf_Element
::TYPE_ARRAY
) {
442 * It's a stream object.
443 * Prepare Contents page attribute for update.
445 $this->_pageDictionary
->touch();
447 $currentPageContents = $this->_pageDictionary
->Contents
;
448 $this->_pageDictionary
->Contents
= new Zend_Pdf_Element_Array();
449 $this->_pageDictionary
->Contents
->items
[] = $currentPageContents;
451 $this->_pageDictionary
->Contents
->touch();
454 if ((!$this->_safeGS
) && (count($this->_pageDictionary
->Contents
->items
) != 0)) {
456 * Page already has some content which is not treated as safe.
458 * Add save/restore GS operators
460 $this->_addProcSet('PDF');
462 $newContentsArray = new Zend_Pdf_Element_Array();
463 $newContentsArray->items
[] = $this->_objFactory
->newStreamObject(" q\n");
464 foreach ($this->_pageDictionary
->Contents
->items
as $contentStream) {
465 $newContentsArray->items
[] = $contentStream;
467 $newContentsArray->items
[] = $this->_objFactory
->newStreamObject(" Q\n");
469 $this->_pageDictionary
->touch();
470 $this->_pageDictionary
->Contents
= $newContentsArray;
472 $this->_safeGS
= true;
475 $this->_pageDictionary
->Contents
->items
[] =
476 $this->_objFactory
->newStreamObject($this->_contents
);
478 $this->_contents
= '';
482 * Prepare page to be rendered into PDF.
484 * @todo Don't forget to close all current graphics operations (like path drawing)
486 * @param Zend_Pdf_ElementFactory_Interface $objFactory
487 * @throws Zend_Pdf_Exception
489 public function render(Zend_Pdf_ElementFactory_Interface
$objFactory)
493 if ($objFactory === $this->_objFactory
) {
494 // Page is already attached to the document.
498 if ($this->_attached
) {
499 require_once 'Zend/Pdf/Exception.php';
500 throw new Zend_Pdf_Exception('Page is attached to one documen, but rendered in context of another.');
502 * @todo Page cloning must be implemented here instead of exception.
503 * PDF objects (ex. fonts) can be shared between pages.
504 * Thus all referenced objects, which can be modified, must be cloned recursively,
505 * to avoid producing wrong object references in a context of source PDF.
510 $objFactory->attach($this->_objFactory
);
519 * @param Zend_Pdf_Color $color
520 * @return Zend_Pdf_Page
522 public function setFillColor(Zend_Pdf_Color
$color)
524 $this->_addProcSet('PDF');
525 $this->_contents
.= $color->instructions(false);
533 * @param Zend_Pdf_Color $color
534 * @return Zend_Pdf_Page
536 public function setLineColor(Zend_Pdf_Color
$color)
538 $this->_addProcSet('PDF');
539 $this->_contents
.= $color->instructions(true);
547 * @param float $width
548 * @return Zend_Pdf_Page
550 public function setLineWidth($width)
552 $this->_addProcSet('PDF');
553 $widthObj = new Zend_Pdf_Element_Numeric($width);
554 $this->_contents
.= $widthObj->toString() . " w\n";
560 * Set line dashing pattern
562 * Pattern is an array of floats: array(on_length, off_length, on_length, off_length, ...)
563 * Phase is shift from the beginning of line.
565 * @param array $pattern
566 * @param array $phase
567 * @return Zend_Pdf_Page
569 public function setLineDashingPattern($pattern, $phase = 0)
571 $this->_addProcSet('PDF');
573 if ($pattern === Zend_Pdf_Page
::LINE_DASHING_SOLID
) {
578 $dashPattern = new Zend_Pdf_Element_Array();
579 $phaseEleemnt = new Zend_Pdf_Element_Numeric($phase);
581 foreach ($pattern as $dashItem) {
582 $dashElement = new Zend_Pdf_Element_Numeric($dashItem);
583 $dashPattern->items
[] = $dashElement;
586 $this->_contents
.= $dashPattern->toString() . ' '
587 . $phaseEleemnt->toString() . " d\n";
595 * @param Zend_Pdf_Resource_Font $font
596 * @param float $fontSize
597 * @return Zend_Pdf_Page
599 public function setFont(Zend_Pdf_Resource_Font
$font, $fontSize)
601 $this->_addProcSet('Text');
602 $fontName = $this->_attachResource('Font', $font);
604 $this->_font
= $font;
605 $this->_fontSize
= $fontSize;
607 $fontNameObj = new Zend_Pdf_Element_Name($fontName);
608 $fontSizeObj = new Zend_Pdf_Element_Numeric($fontSize);
609 $this->_contents
.= $fontNameObj->toString() . ' ' . $fontSizeObj->toString() . " Tf\n";
615 * Set the style to use for future drawing operations on this page
617 * @param Zend_Pdf_Style $style
618 * @return Zend_Pdf_Page
620 public function setStyle(Zend_Pdf_Style
$style)
622 $this->_style
= $style;
624 $this->_addProcSet('Text');
625 $this->_addProcSet('PDF');
626 if ($style->getFont() !== null) {
627 $this->setFont($style->getFont(), $style->getFontSize());
629 $this->_contents
.= $style->instructions($this->_pageDictionary
->Resources
);
635 * Set the transparancy
637 * $alpha == 0 - transparent
638 * $alpha == 1 - opaque
640 * Transparency modes, supported by PDF:
641 * Normal (default), Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight,
642 * SoftLight, Difference, Exclusion
644 * @param float $alpha
645 * @param string $mode
646 * @throws Zend_Pdf_Exception
647 * @return Zend_Pdf_Page
649 public function setAlpha($alpha, $mode = 'Normal')
651 if (!in_array($mode, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge',
652 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion'))) {
653 require_once 'Zend/Pdf/Exception.php';
654 throw new Zend_Pdf_Exception('Unsupported transparency mode.');
656 if (!is_numeric($alpha) ||
$alpha < 0 ||
$alpha > 1) {
657 require_once 'Zend/Pdf/Exception.php';
658 throw new Zend_Pdf_Exception('Alpha value must be numeric between 0 (transparent) and 1 (opaque).');
661 $this->_addProcSet('Text');
662 $this->_addProcSet('PDF');
664 $resources = $this->_pageDictionary
->Resources
;
666 // Check if Resources dictionary contains ExtGState entry
667 if ($resources->ExtGState
=== null) {
669 $resources->ExtGState
= new Zend_Pdf_Element_Dictionary();
671 $resources->ExtGState
->touch();
676 $gStateName = 'GS' . $idCounter++
;
677 } while ($resources->ExtGState
->$gStateName !== null);
680 $gStateDictionary = new Zend_Pdf_Element_Dictionary();
681 $gStateDictionary->Type
= new Zend_Pdf_Element_Name('ExtGState');
682 $gStateDictionary->BM
= new Zend_Pdf_Element_Name($mode);
683 $gStateDictionary->CA
= new Zend_Pdf_Element_Numeric($alpha);
684 $gStateDictionary->ca
= new Zend_Pdf_Element_Numeric($alpha);
686 $resources->ExtGState
->$gStateName = $this->_objFactory
->newObject($gStateDictionary);
688 $gStateNameObj = new Zend_Pdf_Element_Name($gStateName);
689 $this->_contents
.= $gStateNameObj->toString() . " gs\n";
698 * @return Zend_Pdf_Resource_Font $font
700 public function getFont()
706 * Extract resources attached to the page
708 * This method is not intended to be used in userland, but helps to optimize some document wide operations
710 * returns array of Zend_Pdf_Element_Dictionary objects
715 public function extractResources()
717 return $this->_pageDictionary
->Resources
;
721 * Extract fonts attached to the page
723 * returns array of Zend_Pdf_Resource_Font_Extracted objects
727 public function extractFonts()
729 if ($this->_pageDictionary
->Resources
->Font
=== null) {
730 // Page doesn't have any font attached
731 // Return empty array
735 $fontResources = $this->_pageDictionary
->Resources
->Font
;
737 $fontResourcesUnique = array();
738 foreach ($fontResources->getKeys() as $fontResourceName) {
739 $fontDictionary = $fontResources->$fontResourceName;
741 if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
742 $fontDictionary instanceof Zend_Pdf_Element_Object
) ) {
743 // Font dictionary has to be an indirect object or object reference
747 $fontResourcesUnique[$fontDictionary->toString($this->_objFactory
)] = $fontDictionary;
751 require_once 'Zend/Pdf/Exception.php';
752 foreach ($fontResourcesUnique as $resourceReference => $fontDictionary) {
754 // Try to extract font
755 $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
757 $fonts[$resourceReference] = $extractedFont;
758 } catch (Zend_Pdf_Exception
$e) {
759 if ($e->getMessage() != 'Unsupported font type.') {
769 * Extract font attached to the page by specific font name
771 * $fontName should be specified in UTF-8 encoding
773 * @return Zend_Pdf_Resource_Font_Extracted|null
775 public function extractFont($fontName)
777 if ($this->_pageDictionary
->Resources
->Font
=== null) {
778 // Page doesn't have any font attached
782 $fontResources = $this->_pageDictionary
->Resources
->Font
;
784 require_once 'Zend/Pdf/Exception.php';
785 foreach ($fontResources->getKeys() as $fontResourceName) {
786 $fontDictionary = $fontResources->$fontResourceName;
788 if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
789 $fontDictionary instanceof Zend_Pdf_Element_Object
) ) {
790 // Font dictionary has to be an indirect object or object reference
794 if ($fontDictionary->BaseFont
->value
!= $fontName) {
799 // Try to extract font
800 return new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
801 } catch (Zend_Pdf_Exception
$e) {
802 if ($e->getMessage() != 'Unsupported font type.') {
806 // Continue searhing font with specified name
814 * Get current font size
816 * @return float $fontSize
818 public function getFontSize()
820 return $this->_fontSize
;
824 * Return the style, applied to the page.
826 * @return Zend_Pdf_Style|null
828 public function getStyle()
830 return $this->_style
;
835 * Save the graphics state of this page.
836 * This takes a snapshot of the currently applied style, position, clipping area and
837 * any rotation/translation/scaling that has been applied.
839 * @todo check for the open paths
840 * @throws Zend_Pdf_Exception - if a save is performed with an open path
841 * @return Zend_Pdf_Page
843 public function saveGS()
847 $this->_addProcSet('PDF');
848 $this->_contents
.= " q\n";
854 * Restore the graphics state that was saved with the last call to saveGS().
856 * @throws Zend_Pdf_Exception - if there is no previously saved state
857 * @return Zend_Pdf_Page
859 public function restoreGS()
861 if ($this->_saveCount
-- <= 0) {
862 require_once 'Zend/Pdf/Exception.php';
863 throw new Zend_Pdf_Exception('Restoring graphics state which is not saved');
865 $this->_contents
.= " Q\n";
872 * Intersect current clipping area with a circle.
876 * @param float $radius
877 * @param float $startAngle
878 * @param float $endAngle
879 * @return Zend_Pdf_Page
881 public function clipCircle($x, $y, $radius, $startAngle = null, $endAngle = null)
883 $this->clipEllipse($x - $radius, $y - $radius,
884 $x +
$radius, $y +
$radius,
885 $startAngle, $endAngle);
891 * Intersect current clipping area with a polygon.
894 * drawEllipse($x1, $y1, $x2, $y2);
895 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
897 * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
903 * @param float $startAngle
904 * @param float $endAngle
905 * @return Zend_Pdf_Page
907 public function clipEllipse($x1, $y1, $x2, $y2, $startAngle = null, $endAngle = null)
909 $this->_addProcSet('PDF');
925 $xC = new Zend_Pdf_Element_Numeric($x);
926 $yC = new Zend_Pdf_Element_Numeric($y);
928 if ($startAngle !== null) {
929 if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI
*2); }
930 if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI
*2); }
932 if ($startAngle > $endAngle) {
936 $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
937 $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4
);
938 $clipRadius = max($x2 - $x1, $y2 - $y1);
940 for($count = 0; $count <= $clipSectors; $count++
) {
941 $pAngle = $startAngle +
($endAngle - $startAngle)*$count/(float)$clipSectors;
943 $pX = new Zend_Pdf_Element_Numeric($x +
cos($pAngle)*$clipRadius);
944 $pY = new Zend_Pdf_Element_Numeric($y +
sin($pAngle)*$clipRadius);
945 $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
948 $this->_contents
.= $clipPath . "h\nW\nn\n";
951 $xLeft = new Zend_Pdf_Element_Numeric($x1);
952 $xRight = new Zend_Pdf_Element_Numeric($x2);
953 $yUp = new Zend_Pdf_Element_Numeric($y2);
954 $yDown = new Zend_Pdf_Element_Numeric($y1);
956 $xDelta = 2*(M_SQRT2
- 1)*($x2 - $x1)/3.;
957 $yDelta = 2*(M_SQRT2
- 1)*($y2 - $y1)/3.;
958 $xr = new Zend_Pdf_Element_Numeric($x +
$xDelta);
959 $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
960 $yu = new Zend_Pdf_Element_Numeric($y +
$yDelta);
961 $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
963 $this->_contents
.= $xC->toString() . ' ' . $yUp->toString() . " m\n"
964 . $xr->toString() . ' ' . $yUp->toString() . ' '
965 . $xRight->toString() . ' ' . $yu->toString() . ' '
966 . $xRight->toString() . ' ' . $yC->toString() . " c\n"
967 . $xRight->toString() . ' ' . $yd->toString() . ' '
968 . $xr->toString() . ' ' . $yDown->toString() . ' '
969 . $xC->toString() . ' ' . $yDown->toString() . " c\n"
970 . $xl->toString() . ' ' . $yDown->toString() . ' '
971 . $xLeft->toString() . ' ' . $yd->toString() . ' '
972 . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
973 . $xLeft->toString() . ' ' . $yu->toString() . ' '
974 . $xl->toString() . ' ' . $yUp->toString() . ' '
975 . $xC->toString() . ' ' . $yUp->toString() . " c\n"
983 * Intersect current clipping area with a polygon.
985 * @param array $x - array of float (the X co-ordinates of the vertices)
986 * @param array $y - array of float (the Y co-ordinates of the vertices)
987 * @param integer $fillMethod
988 * @return Zend_Pdf_Page
990 public function clipPolygon($x, $y, $fillMethod = Zend_Pdf_Page
::FILL_METHOD_NON_ZERO_WINDING
)
992 $this->_addProcSet('PDF');
995 foreach ($x as $id => $xVal) {
996 $xObj = new Zend_Pdf_Element_Numeric($xVal);
997 $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
1000 $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
1001 $firstPoint = false;
1003 $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
1007 $this->_contents
.= $path;
1009 if ($fillMethod == Zend_Pdf_Page
::FILL_METHOD_NON_ZERO_WINDING
) {
1010 $this->_contents
.= " h\n W\nn\n";
1012 // Even-Odd fill method.
1013 $this->_contents
.= " h\n W*\nn\n";
1020 * Intersect current clipping area with a rectangle.
1026 * @return Zend_Pdf_Page
1028 public function clipRectangle($x1, $y1, $x2, $y2)
1030 $this->_addProcSet('PDF');
1032 $x1Obj = new Zend_Pdf_Element_Numeric($x1);
1033 $y1Obj = new Zend_Pdf_Element_Numeric($y1);
1034 $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
1035 $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
1037 $this->_contents
.= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
1038 . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n"
1045 * Draw a Zend_Pdf_ContentStream at the specified position on the page
1047 * @param ZPdfContentStream $cs
1052 * @return Zend_Pdf_Page
1054 public function drawContentStream($cs, $x1, $y1, $x2, $y2)
1056 /** @todo implementation */
1061 * Draw a circle centered on x, y with a radius of radius.
1063 * Method signatures:
1064 * drawCircle($x, $y, $radius);
1065 * drawCircle($x, $y, $radius, $fillType);
1066 * drawCircle($x, $y, $radius, $startAngle, $endAngle);
1067 * drawCircle($x, $y, $radius, $startAngle, $endAngle, $fillType);
1070 * It's not a really circle, because PDF supports only cubic Bezier curves.
1071 * But _very_ good approximation.
1072 * It differs from a real circle on a maximum 0.00026 radiuses
1073 * (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).
1074 * 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.
1078 * @param float $radius
1079 * @param mixed $param4
1080 * @param mixed $param5
1081 * @param mixed $param6
1082 * @return Zend_Pdf_Page
1084 public function drawCircle($x, $y, $radius, $param4 = null, $param5 = null, $param6 = null)
1086 $this->drawEllipse($x - $radius, $y - $radius,
1087 $x +
$radius, $y +
$radius,
1088 $param4, $param5, $param6);
1094 * Draw an ellipse inside the specified rectangle.
1096 * Method signatures:
1097 * drawEllipse($x1, $y1, $x2, $y2);
1098 * drawEllipse($x1, $y1, $x2, $y2, $fillType);
1099 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
1100 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
1102 * @todo process special cases with $x2-$x1 == 0 or $y2-$y1 == 0
1108 * @param mixed $param5
1109 * @param mixed $param6
1110 * @param mixed $param7
1111 * @return Zend_Pdf_Page
1113 public function drawEllipse($x1, $y1, $x2, $y2, $param5 = null, $param6 = null, $param7 = null)
1115 if ($param5 === null) {
1116 // drawEllipse($x1, $y1, $x2, $y2);
1118 $fillType = Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
;
1119 } else if ($param6 === null) {
1120 // drawEllipse($x1, $y1, $x2, $y2, $fillType);
1122 $fillType = $param5;
1124 // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
1125 // drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
1126 $startAngle = $param5;
1127 $endAngle = $param6;
1129 if ($param7 === null) {
1130 $fillType = Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
;
1132 $fillType = $param7;
1136 $this->_addProcSet('PDF');
1149 $x = ($x1 +
$x2)/2.;
1150 $y = ($y1 +
$y2)/2.;
1152 $xC = new Zend_Pdf_Element_Numeric($x);
1153 $yC = new Zend_Pdf_Element_Numeric($y);
1155 if ($startAngle !== null) {
1156 if ($startAngle != 0) { $startAngle = fmod($startAngle, M_PI
*2); }
1157 if ($endAngle != 0) { $endAngle = fmod($endAngle, M_PI
*2); }
1159 if ($startAngle > $endAngle) {
1160 $endAngle +
= M_PI
*2;
1163 $clipPath = $xC->toString() . ' ' . $yC->toString() . " m\n";
1164 $clipSectors = (int)ceil(($endAngle - $startAngle)/M_PI_4
);
1165 $clipRadius = max($x2 - $x1, $y2 - $y1);
1167 for($count = 0; $count <= $clipSectors; $count++
) {
1168 $pAngle = $startAngle +
($endAngle - $startAngle)*$count/(float)$clipSectors;
1170 $pX = new Zend_Pdf_Element_Numeric($x +
cos($pAngle)*$clipRadius);
1171 $pY = new Zend_Pdf_Element_Numeric($y +
sin($pAngle)*$clipRadius);
1172 $clipPath .= $pX->toString() . ' ' . $pY->toString() . " l\n";
1175 $this->_contents
.= "q\n" . $clipPath . "h\nW\nn\n";
1178 $xLeft = new Zend_Pdf_Element_Numeric($x1);
1179 $xRight = new Zend_Pdf_Element_Numeric($x2);
1180 $yUp = new Zend_Pdf_Element_Numeric($y2);
1181 $yDown = new Zend_Pdf_Element_Numeric($y1);
1183 $xDelta = 2*(M_SQRT2
- 1)*($x2 - $x1)/3.;
1184 $yDelta = 2*(M_SQRT2
- 1)*($y2 - $y1)/3.;
1185 $xr = new Zend_Pdf_Element_Numeric($x +
$xDelta);
1186 $xl = new Zend_Pdf_Element_Numeric($x - $xDelta);
1187 $yu = new Zend_Pdf_Element_Numeric($y +
$yDelta);
1188 $yd = new Zend_Pdf_Element_Numeric($y - $yDelta);
1190 $this->_contents
.= $xC->toString() . ' ' . $yUp->toString() . " m\n"
1191 . $xr->toString() . ' ' . $yUp->toString() . ' '
1192 . $xRight->toString() . ' ' . $yu->toString() . ' '
1193 . $xRight->toString() . ' ' . $yC->toString() . " c\n"
1194 . $xRight->toString() . ' ' . $yd->toString() . ' '
1195 . $xr->toString() . ' ' . $yDown->toString() . ' '
1196 . $xC->toString() . ' ' . $yDown->toString() . " c\n"
1197 . $xl->toString() . ' ' . $yDown->toString() . ' '
1198 . $xLeft->toString() . ' ' . $yd->toString() . ' '
1199 . $xLeft->toString() . ' ' . $yC->toString() . " c\n"
1200 . $xLeft->toString() . ' ' . $yu->toString() . ' '
1201 . $xl->toString() . ' ' . $yUp->toString() . ' '
1202 . $xC->toString() . ' ' . $yUp->toString() . " c\n";
1204 switch ($fillType) {
1205 case Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
:
1206 $this->_contents
.= " B*\n";
1208 case Zend_Pdf_Page
::SHAPE_DRAW_FILL
:
1209 $this->_contents
.= " f*\n";
1211 case Zend_Pdf_Page
::SHAPE_DRAW_STROKE
:
1212 $this->_contents
.= " S\n";
1216 if ($startAngle !== null) {
1217 $this->_contents
.= "Q\n";
1224 * Draw an image at the specified position on the page.
1226 * @param Zend_Pdf_Image $image
1231 * @return Zend_Pdf_Page
1233 public function drawImage(Zend_Pdf_Resource_Image
$image, $x1, $y1, $x2, $y2)
1235 $this->_addProcSet('PDF');
1237 $imageName = $this->_attachResource('XObject', $image);
1238 $imageNameObj = new Zend_Pdf_Element_Name($imageName);
1240 $x1Obj = new Zend_Pdf_Element_Numeric($x1);
1241 $y1Obj = new Zend_Pdf_Element_Numeric($y1);
1242 $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
1243 $heightObj = new Zend_Pdf_Element_Numeric($y2 - $y1);
1245 $this->_contents
.= "q\n"
1246 . '1 0 0 1 ' . $x1Obj->toString() . ' ' . $y1Obj->toString() . " cm\n"
1247 . $widthObj->toString() . ' 0 0 ' . $heightObj->toString() . " 0 0 cm\n"
1248 . $imageNameObj->toString() . " Do\n"
1255 * Draw a LayoutBox at the specified position on the page.
1257 * @param Zend_Pdf_Element_LayoutBox $box
1260 * @return Zend_Pdf_Page
1262 public function drawLayoutBox($box, $x, $y)
1264 /** @todo implementation */
1269 * Draw a line from x1,y1 to x2,y2.
1275 * @return Zend_Pdf_Page
1277 public function drawLine($x1, $y1, $x2, $y2)
1279 $this->_addProcSet('PDF');
1281 $x1Obj = new Zend_Pdf_Element_Numeric($x1);
1282 $y1Obj = new Zend_Pdf_Element_Numeric($y1);
1283 $x2Obj = new Zend_Pdf_Element_Numeric($x2);
1284 $y2Obj = new Zend_Pdf_Element_Numeric($y2);
1286 $this->_contents
.= $x1Obj->toString() . ' ' . $y1Obj->toString() . " m\n"
1287 . $x2Obj->toString() . ' ' . $y2Obj->toString() . " l\n S\n";
1295 * If $fillType is Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE or
1296 * Zend_Pdf_Page::SHAPE_DRAW_FILL, then polygon is automatically closed.
1297 * See detailed description of these methods in a PDF documentation
1298 * (section 4.4.2 Path painting Operators, Filling)
1300 * @param array $x - array of float (the X co-ordinates of the vertices)
1301 * @param array $y - array of float (the Y co-ordinates of the vertices)
1302 * @param integer $fillType
1303 * @param integer $fillMethod
1304 * @return Zend_Pdf_Page
1306 public function drawPolygon($x, $y,
1307 $fillType = Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
,
1308 $fillMethod = Zend_Pdf_Page
::FILL_METHOD_NON_ZERO_WINDING
)
1310 $this->_addProcSet('PDF');
1313 foreach ($x as $id => $xVal) {
1314 $xObj = new Zend_Pdf_Element_Numeric($xVal);
1315 $yObj = new Zend_Pdf_Element_Numeric($y[$id]);
1318 $path = $xObj->toString() . ' ' . $yObj->toString() . " m\n";
1319 $firstPoint = false;
1321 $path .= $xObj->toString() . ' ' . $yObj->toString() . " l\n";
1325 $this->_contents
.= $path;
1327 switch ($fillType) {
1328 case Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
:
1329 if ($fillMethod == Zend_Pdf_Page
::FILL_METHOD_NON_ZERO_WINDING
) {
1330 $this->_contents
.= " b\n";
1332 // Even-Odd fill method.
1333 $this->_contents
.= " b*\n";
1336 case Zend_Pdf_Page
::SHAPE_DRAW_FILL
:
1337 if ($fillMethod == Zend_Pdf_Page
::FILL_METHOD_NON_ZERO_WINDING
) {
1338 $this->_contents
.= " h\n f\n";
1340 // Even-Odd fill method.
1341 $this->_contents
.= " h\n f*\n";
1344 case Zend_Pdf_Page
::SHAPE_DRAW_STROKE
:
1345 $this->_contents
.= " S\n";
1356 * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - fill rectangle and stroke (default)
1357 * Zend_Pdf_Page::SHAPE_DRAW_STROKE - stroke rectangle
1358 * Zend_Pdf_Page::SHAPE_DRAW_FILL - fill rectangle
1364 * @param integer $fillType
1365 * @return Zend_Pdf_Page
1367 public function drawRectangle($x1, $y1, $x2, $y2, $fillType = Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
)
1369 $this->_addProcSet('PDF');
1371 $x1Obj = new Zend_Pdf_Element_Numeric($x1);
1372 $y1Obj = new Zend_Pdf_Element_Numeric($y1);
1373 $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1);
1374 $height2Obj = new Zend_Pdf_Element_Numeric($y2 - $y1);
1376 $this->_contents
.= $x1Obj->toString() . ' ' . $y1Obj->toString() . ' '
1377 . $widthObj->toString() . ' ' . $height2Obj->toString() . " re\n";
1379 switch ($fillType) {
1380 case Zend_Pdf_Page
::SHAPE_DRAW_FILL_AND_STROKE
:
1381 $this->_contents
.= " B*\n";
1383 case Zend_Pdf_Page
::SHAPE_DRAW_FILL
:
1384 $this->_contents
.= " f*\n";
1386 case Zend_Pdf_Page
::SHAPE_DRAW_STROKE
:
1387 $this->_contents
.= " S\n";
1395 * Draw a line of text at the specified position.
1397 * @param string $text
1400 * @param string $charEncoding (optional) Character encoding of source text.
1401 * Defaults to current locale.
1402 * @throws Zend_Pdf_Exception
1403 * @return Zend_Pdf_Page
1405 public function drawText($text, $x, $y, $charEncoding = '')
1407 if ($this->_font
=== null) {
1408 require_once 'Zend/Pdf/Exception.php';
1409 throw new Zend_Pdf_Exception('Font has not been set');
1412 $this->_addProcSet('Text');
1414 $textObj = new Zend_Pdf_Element_String($this->_font
->encodeString($text, $charEncoding));
1415 $xObj = new Zend_Pdf_Element_Numeric($x);
1416 $yObj = new Zend_Pdf_Element_Numeric($y);
1418 $this->_contents
.= "BT\n"
1419 . $xObj->toString() . ' ' . $yObj->toString() . " Td\n"
1420 . $textObj->toString() . " Tj\n"
1427 * Return the height of this page in points.
1431 public function getHeight()
1433 return $this->_pageDictionary
->MediaBox
->items
[3]->value
-
1434 $this->_pageDictionary
->MediaBox
->items
[1]->value
;
1438 * Return the width of this page in points.
1442 public function getWidth()
1444 return $this->_pageDictionary
->MediaBox
->items
[2]->value
-
1445 $this->_pageDictionary
->MediaBox
->items
[0]->value
;
1449 * Close the path by drawing a straight line back to it's beginning.
1451 * @throws Zend_Pdf_Exception - if a path hasn't been started with pathMove()
1452 * @return Zend_Pdf_Page
1454 public function pathClose()
1456 /** @todo implementation */
1461 * Continue the open path in a straight line to the specified position.
1463 * @param float $x - the X co-ordinate to move to
1464 * @param float $y - the Y co-ordinate to move to
1465 * @return Zend_Pdf_Page
1467 public function pathLine($x, $y)
1469 /** @todo implementation */
1474 * Start a new path at the specified position. If a path has already been started,
1475 * move the cursor without drawing a line.
1477 * @param float $x - the X co-ordinate to move to
1478 * @param float $y - the Y co-ordinate to move to
1479 * @return Zend_Pdf_Page
1481 public function pathMove($x, $y)
1483 /** @todo implementation */
1488 * Writes the raw data to the page's content stream.
1490 * Be sure to consult the PDF reference to ensure your syntax is correct. No
1491 * attempt is made to ensure the validity of the stream data.
1493 * @param string $data
1494 * @param string $procSet (optional) Name of ProcSet to add.
1495 * @return Zend_Pdf_Page
1497 public function rawWrite($data, $procSet = null)
1499 if (! empty($procSet)) {
1500 $this->_addProcSet($procSet);
1502 $this->_contents
.= $data;
1510 * @param float $x - the X co-ordinate of rotation point
1511 * @param float $y - the Y co-ordinate of rotation point
1512 * @param float $angle - rotation angle
1513 * @return Zend_Pdf_Page
1515 public function rotate($x, $y, $angle)
1517 $cos = new Zend_Pdf_Element_Numeric(cos($angle));
1518 $sin = new Zend_Pdf_Element_Numeric(sin($angle));
1519 $mSin = new Zend_Pdf_Element_Numeric(-$sin->value
);
1521 $xObj = new Zend_Pdf_Element_Numeric($x);
1522 $yObj = new Zend_Pdf_Element_Numeric($y);
1524 $mXObj = new Zend_Pdf_Element_Numeric(-$x);
1525 $mYObj = new Zend_Pdf_Element_Numeric(-$y);
1528 $this->_addProcSet('PDF');
1529 $this->_contents
.= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
1530 . $cos->toString() . ' ' . $sin->toString() . ' ' . $mSin->toString() . ' ' . $cos->toString() . " 0 0 cm\n"
1531 . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";
1537 * Scale coordination system.
1539 * @param float $xScale - X dimention scale factor
1540 * @param float $yScale - Y dimention scale factor
1541 * @return Zend_Pdf_Page
1543 public function scale($xScale, $yScale)
1545 $xScaleObj = new Zend_Pdf_Element_Numeric($xScale);
1546 $yScaleObj = new Zend_Pdf_Element_Numeric($yScale);
1548 $this->_addProcSet('PDF');
1549 $this->_contents
.= $xScaleObj->toString() . ' 0 0 ' . $yScaleObj->toString() . " 0 0 cm\n";
1555 * Translate coordination system.
1557 * @param float $xShift - X coordinate shift
1558 * @param float $yShift - Y coordinate shift
1559 * @return Zend_Pdf_Page
1561 public function translate($xShift, $yShift)
1563 $xShiftObj = new Zend_Pdf_Element_Numeric($xShift);
1564 $yShiftObj = new Zend_Pdf_Element_Numeric($yShift);
1566 $this->_addProcSet('PDF');
1567 $this->_contents
.= '1 0 0 1 ' . $xShiftObj->toString() . ' ' . $yShiftObj->toString() . " cm\n";
1573 * Translate coordination system.
1575 * @param float $x - the X co-ordinate of axis skew point
1576 * @param float $y - the Y co-ordinate of axis skew point
1577 * @param float $xAngle - X axis skew angle
1578 * @param float $yAngle - Y axis skew angle
1579 * @return Zend_Pdf_Page
1581 public function skew($x, $y, $xAngle, $yAngle)
1583 $tanXObj = new Zend_Pdf_Element_Numeric(tan($xAngle));
1584 $tanYObj = new Zend_Pdf_Element_Numeric(-tan($yAngle));
1586 $xObj = new Zend_Pdf_Element_Numeric($x);
1587 $yObj = new Zend_Pdf_Element_Numeric($y);
1589 $mXObj = new Zend_Pdf_Element_Numeric(-$x);
1590 $mYObj = new Zend_Pdf_Element_Numeric(-$y);
1592 $this->_addProcSet('PDF');
1593 $this->_contents
.= '1 0 0 1 ' . $xObj->toString() . ' ' . $yObj->toString() . " cm\n"
1594 . '1 ' . $tanXObj->toString() . ' ' . $tanYObj->toString() . " 1 0 0 cm\n"
1595 . '1 0 0 1 ' . $mXObj->toString() . ' ' . $mYObj->toString() . " cm\n";