FPDI

Class FPDI.

Defined (1)

The class is defined in the following location(s).

/vendor/setasign/fpdi/fpdi.php  
  1. class FPDI extends FPDF_TPL 
  2. /** 
  3. * FPDI version 
  4. * @string 
  5. */ 
  6. const VERSION = '1.6.1'; 
  7.  
  8. /** 
  9. * Actual filename 
  10. * @var string 
  11. */ 
  12. public $currentFilename; 
  13.  
  14. /** 
  15. * Parser-Objects 
  16. * @var fpdi_pdf_parser[] 
  17. */ 
  18. public $parsers = array(); 
  19.  
  20. /** 
  21. * Current parser 
  22. * @var fpdi_pdf_parser 
  23. */ 
  24. public $currentParser; 
  25.  
  26. /** 
  27. * The name of the last imported page box 
  28. * @var string 
  29. */ 
  30. public $lastUsedPageBox; 
  31.  
  32. /** 
  33. * Object stack 
  34. * @var array 
  35. */ 
  36. protected $_objStack; 
  37.  
  38. /** 
  39. * Done object stack 
  40. * @var array 
  41. */ 
  42. protected $_doneObjStack; 
  43.  
  44. /** 
  45. * Current Object Id. 
  46. * @var integer 
  47. */ 
  48. protected $_currentObjId; 
  49.  
  50. /** 
  51. * Cache for imported pages/template ids 
  52. * @var array 
  53. */ 
  54. protected $_importedPages = array(); 
  55.  
  56. /** 
  57. * Set a source-file. 
  58. * Depending on the PDF version of the used document the PDF version of the resulting document will 
  59. * be adjusted to the higher version. 
  60. * @param string $filename A valid path to the PDF document from which pages should be imported from 
  61. * @return int The number of pages in the document 
  62. */ 
  63. public function setSourceFile($filename) 
  64. $_filename = realpath($filename); 
  65. if (false !== $_filename) 
  66. $filename = $_filename; 
  67.  
  68. $this->currentFilename = $filename; 
  69.  
  70. if (!isset($this->parsers[$filename])) { 
  71. $this->parsers[$filename] = $this->_getPdfParser($filename); 
  72. $this->setPdfVersion( 
  73. max($this->getPdfVersion(), $this->parsers[$filename]->getPdfVersion()) 
  74. ); 
  75.  
  76. $this->currentParser = $this->parsers[$filename]; 
  77.  
  78. return $this->parsers[$filename]->getPageCount(); 
  79.  
  80. /** 
  81. * Returns a PDF parser object 
  82. * @param string $filename 
  83. * @return fpdi_pdf_parser 
  84. */ 
  85. protected function _getPdfParser($filename) 
  86. if (!class_exists('fpdi_pdf_parser')) { 
  87. require_once('fpdi_pdf_parser.php'); 
  88. return new fpdi_pdf_parser($filename); 
  89.  
  90. /** 
  91. * Get the current PDF version. 
  92. * @return string 
  93. */ 
  94. public function getPdfVersion() 
  95. return $this->PDFVersion; 
  96.  
  97. /** 
  98. * Set the PDF version. 
  99. * @param string $version 
  100. */ 
  101. public function setPdfVersion($version = '1.3') 
  102. $this->PDFVersion = sprintf('%.1F', $version); 
  103.  
  104. /** 
  105. * Import a page. 
  106. * The second parameter defines the bounding box that should be used to transform the page into a 
  107. * form XObject. 
  108. * Following values are available: MediaBox, CropBox, BleedBox, TrimBox, ArtBox. 
  109. * If a box is not especially defined its default box will be used: 
  110. * <ul> 
  111. * <li>CropBox: Default -> MediaBox</li> 
  112. * <li>BleedBox: Default -> CropBox</li> 
  113. * <li>TrimBox: Default -> CropBox</li> 
  114. * <li>ArtBox: Default -> CropBox</li> 
  115. * </ul> 
  116. * It is possible to get the used page box by the {@link getLastUsedPageBox()} method. 
  117. * @param int $pageNo The page number 
  118. * @param string $boxName The boundary box to use when transforming the page into a form XObject 
  119. * @param boolean $groupXObject Define the form XObject as a group XObject to support transparency (if used) 
  120. * @return int An id of the imported page/template to use with e.g. fpdf_tpl::useTemplate() 
  121. * @throws LogicException|InvalidArgumentException 
  122. * @see getLastUsedPageBox() 
  123. */ 
  124. public function importPage($pageNo, $boxName = 'CropBox', $groupXObject = true) 
  125. if ($this->_inTpl) { 
  126. throw new LogicException('Please import the desired pages before creating a new template.'); 
  127.  
  128. $fn = $this->currentFilename; 
  129. $boxName = '/' . ltrim($boxName, '/'); 
  130.  
  131. // check if page already imported 
  132. $pageKey = $fn . '-' . ((int)$pageNo) . $boxName; 
  133. if (isset($this->_importedPages[$pageKey])) { 
  134. return $this->_importedPages[$pageKey]; 
  135.  
  136. $parser = $this->parsers[$fn]; 
  137. $parser->setPageNo($pageNo); 
  138.  
  139. if (!in_array($boxName, $parser->availableBoxes)) { 
  140. throw new InvalidArgumentException(sprintf('Unknown box: %s', $boxName)); 
  141.  
  142. $pageBoxes = $parser->getPageBoxes($pageNo, $this->k); 
  143.  
  144. /** 
  145. * MediaBox 
  146. * CropBox: Default -> MediaBox 
  147. * BleedBox: Default -> CropBox 
  148. * TrimBox: Default -> CropBox 
  149. * ArtBox: Default -> CropBox 
  150. */ 
  151. if (!isset($pageBoxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox')) 
  152. $boxName = '/CropBox'; 
  153. if (!isset($pageBoxes[$boxName]) && $boxName == '/CropBox') 
  154. $boxName = '/MediaBox'; 
  155.  
  156. if (!isset($pageBoxes[$boxName])) 
  157. return false; 
  158.  
  159. $this->lastUsedPageBox = $boxName; 
  160.  
  161. $box = $pageBoxes[$boxName]; 
  162.  
  163. $this->tpl++; 
  164. $this->_tpls[$this->tpl] = array(); 
  165. $tpl =& $this->_tpls[$this->tpl]; 
  166. $tpl['parser'] = $parser; 
  167. $tpl['resources'] = $parser->getPageResources(); 
  168. $tpl['buffer'] = $parser->getContent(); 
  169. $tpl['box'] = $box; 
  170. $tpl['groupXObject'] = $groupXObject; 
  171. if ($groupXObject) { 
  172. $this->setPdfVersion(max($this->getPdfVersion(), 1.4)); 
  173.  
  174. // To build an array that can be used by PDF_TPL::useTemplate() 
  175. $this->_tpls[$this->tpl] = array_merge($this->_tpls[$this->tpl], $box); 
  176.  
  177. // An imported page will start at 0, 0 all the time. Translation will be set in _putformxobjects() 
  178. $tpl['x'] = 0; 
  179. $tpl['y'] = 0; 
  180.  
  181. // handle rotated pages 
  182. $rotation = $parser->getPageRotation($pageNo); 
  183. $tpl['_rotationAngle'] = 0; 
  184. if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) { 
  185. $steps = $angle / 90; 
  186.  
  187. $_w = $tpl['w']; 
  188. $_h = $tpl['h']; 
  189. $tpl['w'] = $steps % 2 == 0 ? $_w : $_h; 
  190. $tpl['h'] = $steps % 2 == 0 ? $_h : $_w; 
  191.  
  192. if ($angle < 0) 
  193. $angle += 360; 
  194.  
  195. $tpl['_rotationAngle'] = $angle * -1; 
  196.  
  197. $this->_importedPages[$pageKey] = $this->tpl; 
  198.  
  199. return $this->tpl; 
  200.  
  201. /** 
  202. * Returns the last used page boundary box. 
  203. * @return string The used boundary box: MediaBox, CropBox, BleedBox, TrimBox or ArtBox 
  204. */ 
  205. public function getLastUsedPageBox() 
  206. return $this->lastUsedPageBox; 
  207.  
  208. /** 
  209. * Use a template or imported page in current page or other template. 
  210. * You can use a template in a page or in another template. 
  211. * You can give the used template a new size. All parameters are optional. 
  212. * The width or height is calculated automatically if one is given. If no 
  213. * parameter is given the origin size as defined in beginTemplate() or of 
  214. * the imported page is used. 
  215. * The calculated or used width and height are returned as an array. 
  216. * @param int $tplIdx A valid template-id 
  217. * @param int $x The x-position 
  218. * @param int $y The y-position 
  219. * @param int $w The new width of the template 
  220. * @param int $h The new height of the template 
  221. * @param boolean $adjustPageSize If set to true the current page will be resized to fit the dimensions 
  222. * of the template 
  223. * @return array The height and width of the template (array('w' => ..., 'h' => ...)) 
  224. * @throws LogicException|InvalidArgumentException 
  225. */ 
  226. public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0, $adjustPageSize = false) 
  227. if ($adjustPageSize == true && is_null($x) && is_null($y)) { 
  228. $size = $this->getTemplateSize($tplIdx, $w, $h); 
  229. $orientation = $size['w'] > $size['h'] ? 'L' : 'P'; 
  230. $size = array($size['w'], $size['h']); 
  231.  
  232. if (is_subclass_of($this, 'TCPDF')) { 
  233. $this->setPageFormat($size, $orientation); 
  234. } else { 
  235. $size = $this->_getpagesize($size); 
  236.  
  237. if($orientation != $this->CurOrientation || 
  238. $size[0] != $this->CurPageSize[0] || 
  239. $size[1] != $this->CurPageSize[1] 
  240. ) { 
  241. // New size or orientation 
  242. if ($orientation=='P') { 
  243. $this->w = $size[0]; 
  244. $this->h = $size[1]; 
  245. } else { 
  246. $this->w = $size[1]; 
  247. $this->h = $size[0]; 
  248. $this->wPt = $this->w * $this->k; 
  249. $this->hPt = $this->h * $this->k; 
  250. $this->PageBreakTrigger = $this->h - $this->bMargin; 
  251. $this->CurOrientation = $orientation; 
  252. $this->CurPageSize = $size; 
  253. if (FPDF_VERSION >= 1.8) { 
  254. $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); 
  255. } else { 
  256. $this->PageSizes[$this->page] = array($this->wPt, $this->hPt); 
  257. }  
  258.  
  259. $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values 
  260. $size = parent::useTemplate($tplIdx, $x, $y, $w, $h); 
  261. $this->_out('Q'); 
  262.  
  263. return $size; 
  264.  
  265. /** 
  266. * Copy all imported objects to the resulting document. 
  267. */ 
  268. protected function _putimportedobjects() 
  269. foreach($this->parsers AS $filename => $p) { 
  270. $this->currentParser = $p; 
  271. if (!isset($this->_objStack[$filename]) || !is_array($this->_objStack[$filename])) { 
  272. continue; 
  273. while(($n = key($this->_objStack[$filename])) !== null) { 
  274. try { 
  275. $nObj = $this->currentParser->resolveObject($this->_objStack[$filename][$n][1]); 
  276. } catch (Exception $e) { 
  277. $nObj = array(pdf_parser::TYPE_OBJECT, pdf_parser::TYPE_NULL); 
  278.  
  279. $this->_newobj($this->_objStack[$filename][$n][0]); 
  280.  
  281. if ($nObj[0] == pdf_parser::TYPE_STREAM) { 
  282. $this->_writeValue($nObj); 
  283. } else { 
  284. $this->_writeValue($nObj[1]); 
  285.  
  286. $this->_out("\nendobj"); 
  287. $this->_objStack[$filename][$n] = null; // free memory 
  288. unset($this->_objStack[$filename][$n]); 
  289. reset($this->_objStack[$filename]); 
  290.  
  291. /** 
  292. * Writes the form XObjects to the PDF document. 
  293. */ 
  294. protected function _putformxobjects() 
  295. $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 
  296. reset($this->_tpls); 
  297. foreach($this->_tpls AS $tplIdx => $tpl) { 
  298. $this->_newobj(); 
  299. $currentN = $this->n; // TCPDF/Protection: rem current "n" 
  300.  
  301. $this->_tpls[$tplIdx]['n'] = $this->n; 
  302. $this->_out('<<' . $filter . '/Type /XObject'); 
  303. $this->_out('/Subtype /Form'); 
  304. $this->_out('/FormType 1'); 
  305.  
  306. $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',  
  307. (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,  
  308. (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,  
  309. (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,  
  310. (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k 
  311. )); 
  312.  
  313. $c = 1; 
  314. $s = 0; 
  315. $tx = 0; 
  316. $ty = 0; 
  317.  
  318. if (isset($tpl['box'])) { 
  319. $tx = -$tpl['box']['llx']; 
  320. $ty = -$tpl['box']['lly'];  
  321.  
  322. if ($tpl['_rotationAngle'] <> 0) { 
  323. $angle = $tpl['_rotationAngle'] * M_PI/180; 
  324. $c = cos($angle); 
  325. $s = sin($angle); 
  326.  
  327. switch($tpl['_rotationAngle']) { 
  328. case -90: 
  329. $tx = -$tpl['box']['lly']; 
  330. $ty = $tpl['box']['urx']; 
  331. break; 
  332. case -180: 
  333. $tx = $tpl['box']['urx']; 
  334. $ty = $tpl['box']['ury']; 
  335. break; 
  336. case -270: 
  337. $tx = $tpl['box']['ury']; 
  338. $ty = -$tpl['box']['llx']; 
  339. break; 
  340. } else if ($tpl['x'] != 0 || $tpl['y'] != 0) { 
  341. $tx = -$tpl['x'] * 2; 
  342. $ty = $tpl['y'] * 2; 
  343.  
  344. $tx *= $this->k; 
  345. $ty *= $this->k; 
  346.  
  347. if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) { 
  348. $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',  
  349. $c, $s, -$s, $c, $tx, $ty 
  350. )); 
  351.  
  352. $this->_out('/Resources '); 
  353.  
  354. if (isset($tpl['resources'])) { 
  355. $this->currentParser = $tpl['parser']; 
  356. $this->_writeValue($tpl['resources']); // "n" will be changed 
  357. } else { 
  358.  
  359. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); 
  360. if (isset($this->_res['tpl'][$tplIdx])) { 
  361. $res = $this->_res['tpl'][$tplIdx]; 
  362.  
  363. if (isset($res['fonts']) && count($res['fonts'])) { 
  364. $this->_out('/Font <<'); 
  365. foreach ($res['fonts'] as $font) 
  366. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); 
  367. $this->_out('>>'); 
  368. if (isset($res['images']) && count($res['images']) || 
  369. isset($res['tpls']) && count($res['tpls'])) 
  370. $this->_out('/XObject <<'); 
  371. if (isset($res['images'])) { 
  372. foreach ($res['images'] as $image) 
  373. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); 
  374. if (isset($res['tpls'])) { 
  375. foreach ($res['tpls'] as $i => $_tpl) 
  376. $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R'); 
  377. $this->_out('>>'); 
  378. $this->_out('>>'); 
  379.  
  380. if (isset($tpl['groupXObject']) && $tpl['groupXObject']) { 
  381. $this->_out('/Group <</Type/Group/S/Transparency>>'); 
  382.  
  383. $newN = $this->n; // TCPDF: rem new "n" 
  384. $this->n = $currentN; // TCPDF: reset to current "n" 
  385.  
  386. $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; 
  387.  
  388. if (is_subclass_of($this, 'TCPDF')) { 
  389. $buffer = $this->_getrawstream($buffer); 
  390. $this->_out('/Length ' . strlen($buffer) . ' >>'); 
  391. $this->_out("stream\n" . $buffer . "\nendstream"); 
  392. } else { 
  393. $this->_out('/Length ' . strlen($buffer) . ' >>'); 
  394. $this->_putstream($buffer); 
  395. $this->_out('endobj'); 
  396. $this->n = $newN; // TCPDF: reset to new "n" 
  397.  
  398. $this->_putimportedobjects(); 
  399.  
  400. /** 
  401. * Creates and optionally write the object definition to the document. 
  402. * Rewritten to handle existing own defined objects 
  403. * @param bool $objId 
  404. * @param bool $onlyNewObj 
  405. * @return bool|int 
  406. */ 
  407. public function _newobj($objId = false, $onlyNewObj = false) 
  408. if (!$objId) { 
  409. $objId = ++$this->n; 
  410.  
  411. // Begin a new object 
  412. if (!$onlyNewObj) { 
  413. $this->offsets[$objId] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer); 
  414. $this->_out($objId . ' 0 obj'); 
  415. $this->_currentObjId = $objId; // for later use with encryption 
  416.  
  417. return $objId; 
  418.  
  419. /** 
  420. * Writes a PDF value to the resulting document. 
  421. * Needed to rebuild the source document 
  422. * @param mixed $value A PDF-Value. Structure of values see cases in this method 
  423. */ 
  424. protected function _writeValue(&$value) 
  425. if (is_subclass_of($this, 'TCPDF')) { 
  426. parent::_prepareValue($value); 
  427.  
  428. switch ($value[0]) { 
  429.  
  430. case pdf_parser::TYPE_TOKEN: 
  431. $this->_straightOut($value[1] . ' '); 
  432. break; 
  433. case pdf_parser::TYPE_NUMERIC: 
  434. case pdf_parser::TYPE_REAL: 
  435. if (is_float($value[1]) && $value[1] != 0) { 
  436. $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' '); 
  437. } else { 
  438. $this->_straightOut($value[1] . ' '); 
  439. break; 
  440.  
  441. case pdf_parser::TYPE_ARRAY: 
  442.  
  443. // An array. Output the proper 
  444. // structure and move on. 
  445.  
  446. $this->_straightOut('['); 
  447. for ($i = 0; $i < count($value[1]); $i++) { 
  448. $this->_writeValue($value[1][$i]); 
  449.  
  450. $this->_out(']'); 
  451. break; 
  452.  
  453. case pdf_parser::TYPE_DICTIONARY: 
  454.  
  455. // A dictionary. 
  456. $this->_straightOut('<<'); 
  457.  
  458. reset ($value[1]); 
  459.  
  460. while (list($k, $v) = each($value[1])) { 
  461. $this->_straightOut($k . ' '); 
  462. $this->_writeValue($v); 
  463.  
  464. $this->_straightOut('>>'); 
  465. break; 
  466.  
  467. case pdf_parser::TYPE_OBJREF: 
  468.  
  469. // An indirect object reference 
  470. // Fill the object stack if needed 
  471. $cpfn =& $this->currentParser->filename; 
  472. if (!isset($this->_doneObjStack[$cpfn][$value[1]])) { 
  473. $this->_newobj(false, true); 
  474. $this->_objStack[$cpfn][$value[1]] = array($this->n, $value); 
  475. $this->_doneObjStack[$cpfn][$value[1]] = array($this->n, $value); 
  476. $objId = $this->_doneObjStack[$cpfn][$value[1]][0]; 
  477.  
  478. $this->_out($objId . ' 0 R'); 
  479. break; 
  480.  
  481. case pdf_parser::TYPE_STRING: 
  482.  
  483. // A string. 
  484. $this->_straightOut('(' . $value[1] . ')'); 
  485.  
  486. break; 
  487.  
  488. case pdf_parser::TYPE_STREAM: 
  489.  
  490. // A stream. First, output the 
  491. // stream dictionary, then the 
  492. // stream data itself. 
  493. $this->_writeValue($value[1]); 
  494. $this->_out('stream'); 
  495. $this->_out($value[2][1]); 
  496. $this->_straightOut("endstream"); 
  497. break; 
  498.  
  499. case pdf_parser::TYPE_HEX: 
  500. $this->_straightOut('<' . $value[1] . '>'); 
  501. break; 
  502.  
  503. case pdf_parser::TYPE_BOOLEAN: 
  504. $this->_straightOut($value[1] ? 'true ' : 'false '); 
  505. break; 
  506.  
  507. case pdf_parser::TYPE_NULL: 
  508. // The null object. 
  509. $this->_straightOut('null '); 
  510. break; 
  511.  
  512.  
  513. /** 
  514. * Modified _out() method so not each call will add a newline to the output. 
  515. */ 
  516. protected function _straightOut($s) 
  517. if (!is_subclass_of($this, 'TCPDF')) { 
  518. if ($this->state == 2) { 
  519. $this->pages[$this->page] .= $s; 
  520. } else { 
  521. $this->buffer .= $s; 
  522.  
  523. } else { 
  524. if ($this->state == 2) { 
  525. if ($this->inxobj) { 
  526. // we are inside an XObject template 
  527. $this->xobjects[$this->xobjid]['outdata'] .= $s; 
  528. } else if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 
  529. // puts data before page footer 
  530. $pagebuff = $this->getPageBuffer($this->page); 
  531. $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 
  532. $footer = substr($pagebuff, -$this->footerlen[$this->page]); 
  533. $this->setPageBuffer($this->page, $page . $s . $footer); 
  534. // update footer position 
  535. $this->footerpos[$this->page] += strlen($s); 
  536. } else { 
  537. // set page data 
  538. $this->setPageBuffer($this->page, $s, true); 
  539. } else if ($this->state > 0) { 
  540. // set general data 
  541. $this->setBuffer($s); 
  542.  
  543. /** 
  544. * Ends the document 
  545. * Overwritten to close opened parsers 
  546. */ 
  547. public function _enddoc() 
  548. parent::_enddoc(); 
  549. $this->_closeParsers(); 
  550.  
  551. /** 
  552. * Close all files opened by parsers. 
  553. * @return boolean 
  554. */ 
  555. protected function _closeParsers() 
  556. if ($this->state > 2) { 
  557. $this->cleanUp(); 
  558. return true; 
  559.  
  560. return false; 
  561.  
  562. /** 
  563. * Removes cycled references and closes the file handles of the parser objects. 
  564. */ 
  565. public function cleanUp() 
  566. while (($parser = array_pop($this->parsers)) !== null) { 
  567. /** 
  568. * @var fpdi_pdf_parser $parser 
  569. */ 
  570. $parser->closeFile();