1<?php 2 3namespace setasign\Fpdi\Tcpdf; 4 5use setasign\Fpdi\FpdiTrait; 6use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException; 7use setasign\Fpdi\PdfParser\Filter\AsciiHex; 8use setasign\Fpdi\PdfParser\PdfParserException; 9use setasign\Fpdi\PdfParser\Type\PdfHexString; 10use setasign\Fpdi\PdfParser\Type\PdfIndirectObject; 11use setasign\Fpdi\PdfParser\Type\PdfNull; 12use setasign\Fpdi\PdfParser\Type\PdfNumeric; 13use setasign\Fpdi\PdfParser\Type\PdfStream; 14use setasign\Fpdi\PdfParser\Type\PdfString; 15use setasign\Fpdi\PdfParser\Type\PdfType; 16use setasign\Fpdi\PdfParser\Type\PdfTypeException; 17 18/** 19 * Class Fpdi 20 * 21 * This class let you import pages of existing PDF documents into a reusable structure for TCPDF. 22 * 23 * @package setasign\Fpdi 24 */ 25class Fpdi extends \TCPDF 26{ 27 use FpdiTrait { 28 writePdfType as fpdiWritePdfType; 29 useImportedPage as fpdiUseImportedPage; 30 } 31 32 /** 33 * FPDI version 34 * 35 * @string 36 */ 37 const VERSION = '2.3.1'; 38 39 /** 40 * A counter for template ids. 41 * 42 * @var int 43 */ 44 protected $templateId = 0; 45 46 /** 47 * The currently used object number. 48 * 49 * @var int 50 */ 51 protected $currentObjectNumber; 52 53 protected function _enddoc() 54 { 55 parent::_enddoc(); 56 $this->cleanUp(); 57 } 58 59 /** 60 * Get the next template id. 61 * 62 * @return int 63 */ 64 protected function getNextTemplateId() 65 { 66 return $this->templateId++; 67 } 68 69 /** 70 * Draws an imported page onto the page or another template. 71 * 72 * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the 73 * aspect ratio. 74 * 75 * @param mixed $tpl The template id 76 * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array 77 * with the keys "x", "y", "width", "height", "adjustPageSize". 78 * @param float|int $y The ordinate of upper-left corner. 79 * @param float|int|null $width The width. 80 * @param float|int|null $height The height. 81 * @param bool $adjustPageSize 82 * @return array The size 83 * @see FpdiTrait::getTemplateSize() 84 */ 85 public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) 86 { 87 return $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize); 88 } 89 90 /** 91 * Draws an imported page onto the page. 92 * 93 * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the 94 * aspect ratio. 95 * 96 * @param mixed $pageId The page id 97 * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array 98 * with the keys "x", "y", "width", "height", "adjustPageSize". 99 * @param float|int $y The ordinate of upper-left corner. 100 * @param float|int|null $width The width. 101 * @param float|int|null $height The height. 102 * @param bool $adjustPageSize 103 * @return array The size. 104 * @see Fpdi::getTemplateSize() 105 */ 106 public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) 107 { 108 $size = $this->fpdiUseImportedPage($pageId, $x, $y, $width, $height, $adjustPageSize); 109 if ($this->inxobj) { 110 $importedPage = $this->importedPages[$pageId]; 111 $this->xobjects[$this->xobjid]['importedPages'][$importedPage['id']] = $pageId; 112 } 113 114 return $size; 115 } 116 117 /** 118 * Get the size of an imported page. 119 * 120 * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the 121 * aspect ratio. 122 * 123 * @param mixed $tpl The template id 124 * @param float|int|null $width The width. 125 * @param float|int|null $height The height. 126 * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) 127 */ 128 public function getTemplateSize($tpl, $width = null, $height = null) 129 { 130 return $this->getImportedPageSize($tpl, $width, $height); 131 } 132 133 /** 134 * @inheritdoc 135 */ 136 protected function _getxobjectdict() 137 { 138 $out = parent::_getxobjectdict(); 139 140 foreach ($this->importedPages as $key => $pageData) { 141 $out .= '/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R '; 142 } 143 144 return $out; 145 } 146 147 /** 148 * @inheritdoc 149 * @throws CrossReferenceException 150 * @throws PdfParserException 151 */ 152 protected function _putxobjects() 153 { 154 foreach ($this->importedPages as $key => $pageData) { 155 $this->currentObjectNumber = $this->_newobj(); 156 $this->importedPages[$key]['objectNumber'] = $this->currentObjectNumber; 157 $this->currentReaderId = $pageData['readerId']; 158 $this->writePdfType($pageData['stream']); 159 $this->_put('endobj'); 160 } 161 162 foreach (\array_keys($this->readers) as $readerId) { 163 $parser = $this->getPdfReader($readerId)->getParser(); 164 $this->currentReaderId = $readerId; 165 166 while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) { 167 try { 168 $object = $parser->getIndirectObject($objectNumber); 169 170 } catch (CrossReferenceException $e) { 171 if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) { 172 $object = PdfIndirectObject::create($objectNumber, 0, new PdfNull()); 173 } else { 174 throw $e; 175 } 176 } 177 178 $this->writePdfType($object); 179 } 180 } 181 182 // let's prepare resources for imported pages in templates 183 foreach ($this->xobjects as $xObjectId => $data) { 184 if (!isset($data['importedPages'])) { 185 continue; 186 } 187 188 foreach ($data['importedPages'] as $id => $pageKey) { 189 $page = $this->importedPages[$pageKey]; 190 $this->xobjects[$xObjectId]['xobjects'][$id] = ['n' => $page['objectNumber']]; 191 } 192 } 193 194 195 parent::_putxobjects(); 196 $this->currentObjectNumber = null; 197 } 198 199 /** 200 * Append content to the buffer of TCPDF. 201 * 202 * @param string $s 203 * @param bool $newLine 204 */ 205 protected function _put($s, $newLine = true) 206 { 207 if ($newLine) { 208 $this->setBuffer($s . "\n"); 209 } else { 210 $this->setBuffer($s); 211 } 212 } 213 214 /** 215 * Begin a new object and return the object number. 216 * 217 * @param int|string $objid Object ID (leave empty to get a new ID). 218 * @return int object number 219 */ 220 protected function _newobj($objid = '') 221 { 222 $this->_out($this->_getobj($objid)); 223 return $this->n; 224 } 225 226 /** 227 * Writes a PdfType object to the resulting buffer. 228 * 229 * @param PdfType $value 230 * @throws PdfTypeException 231 */ 232 protected function writePdfType(PdfType $value) 233 { 234 if (!$this->encrypted) { 235 $this->fpdiWritePdfType($value); 236 return; 237 } 238 239 if ($value instanceof PdfString) { 240 $string = PdfString::unescape($value->value); 241 $string = $this->_encrypt_data($this->currentObjectNumber, $string); 242 $value->value = \TCPDF_STATIC::_escape($string); 243 244 } elseif ($value instanceof PdfHexString) { 245 $filter = new AsciiHex(); 246 $string = $filter->decode($value->value); 247 $string = $this->_encrypt_data($this->currentObjectNumber, $string); 248 $value->value = $filter->encode($string, true); 249 250 } elseif ($value instanceof PdfStream) { 251 $stream = $value->getStream(); 252 $stream = $this->_encrypt_data($this->currentObjectNumber, $stream); 253 $dictionary = $value->value; 254 $dictionary->value['Length'] = PdfNumeric::create(\strlen($stream)); 255 $value = PdfStream::create($dictionary, $stream); 256 257 } elseif ($value instanceof PdfIndirectObject) { 258 /** 259 * @var $value PdfIndirectObject 260 */ 261 $this->currentObjectNumber = $this->objectMap[$this->currentReaderId][$value->objectNumber]; 262 } 263 264 $this->fpdiWritePdfType($value); 265 } 266}