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