549 linhas
17KB

  1. <?php
  2. /**
  3. * This file is part of FPDI
  4. *
  5. * @package FPDI
  6. * @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com)
  7. * @license http://opensource.org/licenses/mit-license The MIT License
  8. * @version 1.6.1
  9. */
  10. if (!class_exists('fpdi_bridge')) {
  11. require_once('fpdi_bridge.php');
  12. }
  13. /**
  14. * Class FPDF_TPL
  15. */
  16. class FPDF_TPL extends fpdi_bridge
  17. {
  18. /**
  19. * Array of template data
  20. *
  21. * @var array
  22. */
  23. protected $_tpls = array();
  24. /**
  25. * Current Template-Id
  26. *
  27. * @var int
  28. */
  29. public $tpl = 0;
  30. /**
  31. * "In Template"-Flag
  32. *
  33. * @var boolean
  34. */
  35. protected $_inTpl = false;
  36. /**
  37. * Name prefix of templates used in Resources dictionary
  38. *
  39. * @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an /
  40. */
  41. public $tplPrefix = "/TPL";
  42. /**
  43. * Resources used by templates and pages
  44. *
  45. * @var array
  46. */
  47. protected $_res = array();
  48. /**
  49. * Last used template data
  50. *
  51. * @var array
  52. */
  53. public $lastUsedTemplateData = array();
  54. /**
  55. * Start a template.
  56. *
  57. * This method starts a template. You can give own coordinates to build an own sized
  58. * template. Pay attention, that the margins are adapted to the new template size.
  59. * If you want to write outside the template, for example to build a clipped template,
  60. * you have to set the margins and "cursor"-position manual after beginTemplate()-call.
  61. *
  62. * If no parameter is given, the template uses the current page-size.
  63. * The method returns an id of the current template. This id is used later for using this template.
  64. * Warning: A created template is saved in the resulting PDF at all events. Also if you don't use it after creation!
  65. *
  66. * @param int $x The x-coordinate given in user-unit
  67. * @param int $y The y-coordinate given in user-unit
  68. * @param int $w The width given in user-unit
  69. * @param int $h The height given in user-unit
  70. * @return int The id of new created template
  71. * @throws LogicException
  72. */
  73. public function beginTemplate($x = null, $y = null, $w = null, $h = null)
  74. {
  75. if (is_subclass_of($this, 'TCPDF')) {
  76. throw new LogicException('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.');
  77. }
  78. if ($this->page <= 0) {
  79. throw new LogicException("You have to add at least a page first!");
  80. }
  81. if ($x == null)
  82. $x = 0;
  83. if ($y == null)
  84. $y = 0;
  85. if ($w == null)
  86. $w = $this->w;
  87. if ($h == null)
  88. $h = $this->h;
  89. // Save settings
  90. $this->tpl++;
  91. $tpl =& $this->_tpls[$this->tpl];
  92. $tpl = array(
  93. 'o_x' => $this->x,
  94. 'o_y' => $this->y,
  95. 'o_AutoPageBreak' => $this->AutoPageBreak,
  96. 'o_bMargin' => $this->bMargin,
  97. 'o_tMargin' => $this->tMargin,
  98. 'o_lMargin' => $this->lMargin,
  99. 'o_rMargin' => $this->rMargin,
  100. 'o_h' => $this->h,
  101. 'o_w' => $this->w,
  102. 'o_FontFamily' => $this->FontFamily,
  103. 'o_FontStyle' => $this->FontStyle,
  104. 'o_FontSizePt' => $this->FontSizePt,
  105. 'o_FontSize' => $this->FontSize,
  106. 'buffer' => '',
  107. 'x' => $x,
  108. 'y' => $y,
  109. 'w' => $w,
  110. 'h' => $h
  111. );
  112. $this->SetAutoPageBreak(false);
  113. // Define own high and width to calculate correct positions
  114. $this->h = $h;
  115. $this->w = $w;
  116. $this->_inTpl = true;
  117. $this->SetXY($x + $this->lMargin, $y + $this->tMargin);
  118. $this->SetRightMargin($this->w - $w + $this->rMargin);
  119. if ($this->CurrentFont) {
  120. $fontKey = $this->FontFamily . $this->FontStyle;
  121. if ($fontKey) {
  122. $this->_res['tpl'][$this->tpl]['fonts'][$fontKey] =& $this->fonts[$fontKey];
  123. $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  124. }
  125. }
  126. return $this->tpl;
  127. }
  128. /**
  129. * End template.
  130. *
  131. * This method ends a template and reset initiated variables collected in {@link beginTemplate()}.
  132. *
  133. * @return int|boolean If a template is opened, the id is returned. If not a false is returned.
  134. */
  135. public function endTemplate()
  136. {
  137. if (is_subclass_of($this, 'TCPDF')) {
  138. $args = func_get_args();
  139. return call_user_func_array(array($this, 'TCPDF::endTemplate'), $args);
  140. }
  141. if ($this->_inTpl) {
  142. $this->_inTpl = false;
  143. $tpl = $this->_tpls[$this->tpl];
  144. $this->SetXY($tpl['o_x'], $tpl['o_y']);
  145. $this->tMargin = $tpl['o_tMargin'];
  146. $this->lMargin = $tpl['o_lMargin'];
  147. $this->rMargin = $tpl['o_rMargin'];
  148. $this->h = $tpl['o_h'];
  149. $this->w = $tpl['o_w'];
  150. $this->SetAutoPageBreak($tpl['o_AutoPageBreak'], $tpl['o_bMargin']);
  151. $this->FontFamily = $tpl['o_FontFamily'];
  152. $this->FontStyle = $tpl['o_FontStyle'];
  153. $this->FontSizePt = $tpl['o_FontSizePt'];
  154. $this->FontSize = $tpl['o_FontSize'];
  155. $fontKey = $this->FontFamily . $this->FontStyle;
  156. if ($fontKey)
  157. $this->CurrentFont =& $this->fonts[$fontKey];
  158. return $this->tpl;
  159. } else {
  160. return false;
  161. }
  162. }
  163. /**
  164. * Use a template in current page or other template.
  165. *
  166. * You can use a template in a page or in another template.
  167. * You can give the used template a new size.
  168. * All parameters are optional. The width or height is calculated automatically
  169. * if one is given. If no parameter is given the origin size as defined in
  170. * {@link beginTemplate()} method is used.
  171. *
  172. * The calculated or used width and height are returned as an array.
  173. *
  174. * @param int $tplIdx A valid template-id
  175. * @param int $x The x-position
  176. * @param int $y The y-position
  177. * @param int $w The new width of the template
  178. * @param int $h The new height of the template
  179. * @return array The height and width of the template (array('w' => ..., 'h' => ...))
  180. * @throws LogicException|InvalidArgumentException
  181. */
  182. public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0)
  183. {
  184. if ($this->page <= 0) {
  185. throw new LogicException('You have to add at least a page first!');
  186. }
  187. if (!isset($this->_tpls[$tplIdx])) {
  188. throw new InvalidArgumentException('Template does not exist!');
  189. }
  190. if ($this->_inTpl) {
  191. $this->_res['tpl'][$this->tpl]['tpls'][$tplIdx] =& $this->_tpls[$tplIdx];
  192. }
  193. $tpl = $this->_tpls[$tplIdx];
  194. $_w = $tpl['w'];
  195. $_h = $tpl['h'];
  196. if ($x == null) {
  197. $x = 0;
  198. }
  199. if ($y == null) {
  200. $y = 0;
  201. }
  202. $x += $tpl['x'];
  203. $y += $tpl['y'];
  204. $wh = $this->getTemplateSize($tplIdx, $w, $h);
  205. $w = $wh['w'];
  206. $h = $wh['h'];
  207. $tplData = array(
  208. 'x' => $this->x,
  209. 'y' => $this->y,
  210. 'w' => $w,
  211. 'h' => $h,
  212. 'scaleX' => ($w / $_w),
  213. 'scaleY' => ($h / $_h),
  214. 'tx' => $x,
  215. 'ty' => ($this->h - $y - $h),
  216. 'lty' => ($this->h - $y - $h) - ($this->h - $_h) * ($h / $_h)
  217. );
  218. $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm',
  219. $tplData['scaleX'], $tplData['scaleY'], $tplData['tx'] * $this->k, $tplData['ty'] * $this->k)
  220. ); // Translate
  221. $this->_out(sprintf('%s%d Do Q', $this->tplPrefix, $tplIdx));
  222. $this->lastUsedTemplateData = $tplData;
  223. return array('w' => $w, 'h' => $h);
  224. }
  225. /**
  226. * Get the calculated size of a template.
  227. *
  228. * If one size is given, this method calculates the other one.
  229. *
  230. * @param int $tplIdx A valid template-id
  231. * @param int $w The width of the template
  232. * @param int $h The height of the template
  233. * @return array The height and width of the template (array('w' => ..., 'h' => ...))
  234. */
  235. public function getTemplateSize($tplIdx, $w = 0, $h = 0)
  236. {
  237. if (!isset($this->_tpls[$tplIdx]))
  238. return false;
  239. $tpl = $this->_tpls[$tplIdx];
  240. $_w = $tpl['w'];
  241. $_h = $tpl['h'];
  242. if ($w == 0 && $h == 0) {
  243. $w = $_w;
  244. $h = $_h;
  245. }
  246. if ($w == 0)
  247. $w = $h * $_w / $_h;
  248. if($h == 0)
  249. $h = $w * $_h / $_w;
  250. return array("w" => $w, "h" => $h);
  251. }
  252. /**
  253. * Sets the font used to print character strings.
  254. *
  255. * See FPDF/TCPDF documentation.
  256. *
  257. * @see http://fpdf.org/en/doc/setfont.htm
  258. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#afd56e360c43553830d543323e81bc045
  259. */
  260. public function SetFont($family, $style = '', $size = null, $fontfile = '', $subset = 'default', $out = true)
  261. {
  262. if (is_subclass_of($this, 'TCPDF')) {
  263. $args = func_get_args();
  264. return call_user_func_array(array($this, 'TCPDF::SetFont'), $args);
  265. }
  266. parent::SetFont($family, $style, $size);
  267. $fontkey = $this->FontFamily . $this->FontStyle;
  268. if ($this->_inTpl) {
  269. $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey];
  270. } else {
  271. $this->_res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey];
  272. }
  273. }
  274. /**
  275. * Puts an image.
  276. *
  277. * See FPDF/TCPDF documentation.
  278. *
  279. * @see http://fpdf.org/en/doc/image.htm
  280. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a714c2bee7d6b39d4d6d304540c761352
  281. */
  282. public function Image(
  283. $file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false,
  284. $dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false,
  285. $hidden = false, $fitonpage = false, $alt = false, $altimgs = array()
  286. )
  287. {
  288. if (is_subclass_of($this, 'TCPDF')) {
  289. $args = func_get_args();
  290. return call_user_func_array(array($this, 'TCPDF::Image'), $args);
  291. }
  292. $ret = parent::Image($file, $x, $y, $w, $h, $type, $link);
  293. if ($this->_inTpl) {
  294. $this->_res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file];
  295. } else {
  296. $this->_res['page'][$this->page]['images'][$file] =& $this->images[$file];
  297. }
  298. return $ret;
  299. }
  300. /**
  301. * Adds a new page to the document.
  302. *
  303. * See FPDF/TCPDF documentation.
  304. *
  305. * This method cannot be used if you'd started a template.
  306. *
  307. * @see http://fpdf.org/en/doc/addpage.htm
  308. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a5171e20b366b74523709d84c349c1ced
  309. */
  310. public function AddPage($orientation = '', $format = '', $rotationOrKeepmargins = false, $tocpage = false)
  311. {
  312. if (is_subclass_of($this, 'TCPDF')) {
  313. $args = func_get_args();
  314. return call_user_func_array(array($this, 'TCPDF::AddPage'), $args);
  315. }
  316. if ($this->_inTpl) {
  317. throw new LogicException('Adding pages in templates is not possible!');
  318. }
  319. parent::AddPage($orientation, $format, $rotationOrKeepmargins);
  320. }
  321. /**
  322. * Puts a link on a rectangular area of the page.
  323. *
  324. * Overwritten because adding links in a template will not work.
  325. *
  326. * @see http://fpdf.org/en/doc/link.htm
  327. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ab87bf1826384fbfe30eb499d42f1d994
  328. */
  329. public function Link($x, $y, $w, $h, $link, $spaces = 0)
  330. {
  331. if (is_subclass_of($this, 'TCPDF')) {
  332. $args = func_get_args();
  333. return call_user_func_array(array($this, 'TCPDF::Link'), $args);
  334. }
  335. if ($this->_inTpl) {
  336. throw new LogicException('Using links in templates is not posible!');
  337. }
  338. parent::Link($x, $y, $w, $h, $link);
  339. }
  340. /**
  341. * Creates a new internal link and returns its identifier.
  342. *
  343. * Overwritten because adding links in a template will not work.
  344. *
  345. * @see http://fpdf.org/en/doc/addlink.htm
  346. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a749522038ed7786c3e1701435dcb891e
  347. */
  348. public function AddLink()
  349. {
  350. if (is_subclass_of($this, 'TCPDF')) {
  351. $args = func_get_args();
  352. return call_user_func_array(array($this, 'TCPDF::AddLink'), $args);
  353. }
  354. if ($this->_inTpl) {
  355. throw new LogicException('Adding links in templates is not possible!');
  356. }
  357. return parent::AddLink();
  358. }
  359. /**
  360. * Defines the page and position a link points to.
  361. *
  362. * Overwritten because adding links in a template will not work.
  363. *
  364. * @see http://fpdf.org/en/doc/setlink.htm
  365. * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ace5be60e7857953ea5e2b89cb90df0ae
  366. */
  367. public function SetLink($link, $y = 0, $page = -1)
  368. {
  369. if (is_subclass_of($this, 'TCPDF')) {
  370. $args = func_get_args();
  371. return call_user_func_array(array($this, 'TCPDF::SetLink'), $args);
  372. }
  373. if ($this->_inTpl) {
  374. throw new LogicException('Setting links in templates is not possible!');
  375. }
  376. parent::SetLink($link, $y, $page);
  377. }
  378. /**
  379. * Writes the form XObjects to the PDF document.
  380. */
  381. protected function _putformxobjects()
  382. {
  383. $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
  384. reset($this->_tpls);
  385. foreach($this->_tpls AS $tplIdx => $tpl) {
  386. $this->_newobj();
  387. $this->_tpls[$tplIdx]['n'] = $this->n;
  388. $this->_out('<<'.$filter.'/Type /XObject');
  389. $this->_out('/Subtype /Form');
  390. $this->_out('/FormType 1');
  391. $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
  392. // llx
  393. $tpl['x'] * $this->k,
  394. // lly
  395. -$tpl['y'] * $this->k,
  396. // urx
  397. ($tpl['w'] + $tpl['x']) * $this->k,
  398. // ury
  399. ($tpl['h'] - $tpl['y']) * $this->k
  400. ));
  401. if ($tpl['x'] != 0 || $tpl['y'] != 0) {
  402. $this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]',
  403. -$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2
  404. ));
  405. }
  406. $this->_out('/Resources ');
  407. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  408. if (isset($this->_res['tpl'][$tplIdx])) {
  409. $res = $this->_res['tpl'][$tplIdx];
  410. if (isset($res['fonts']) && count($res['fonts'])) {
  411. $this->_out('/Font <<');
  412. foreach($res['fonts'] as $font) {
  413. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  414. }
  415. $this->_out('>>');
  416. }
  417. if(isset($res['images']) || isset($res['tpls'])) {
  418. $this->_out('/XObject <<');
  419. if (isset($res['images'])) {
  420. foreach($res['images'] as $image)
  421. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  422. }
  423. if (isset($res['tpls'])) {
  424. foreach($res['tpls'] as $i => $_tpl)
  425. $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
  426. }
  427. $this->_out('>>');
  428. }
  429. }
  430. $this->_out('>>');
  431. $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
  432. $this->_out('/Length ' . strlen($buffer) . ' >>');
  433. $this->_putstream($buffer);
  434. $this->_out('endobj');
  435. }
  436. }
  437. /**
  438. * Output images.
  439. *
  440. * Overwritten to add {@link _putformxobjects()} after _putimages().
  441. */
  442. public function _putimages()
  443. {
  444. parent::_putimages();
  445. $this->_putformxobjects();
  446. }
  447. /**
  448. * Writes the references of XObject resources to the document.
  449. *
  450. * Overwritten to add the the templates to the XObject resource dictionary.
  451. */
  452. public function _putxobjectdict()
  453. {
  454. parent::_putxobjectdict();
  455. foreach($this->_tpls as $tplIdx => $tpl) {
  456. $this->_out(sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']));
  457. }
  458. }
  459. /**
  460. * Writes bytes to the resulting document.
  461. *
  462. * Overwritten to delegate the data to the template buffer.
  463. *
  464. * @param string $s
  465. */
  466. public function _out($s)
  467. {
  468. if ($this->state == 2 && $this->_inTpl) {
  469. $this->_tpls[$this->tpl]['buffer'] .= $s . "\n";
  470. } else {
  471. parent::_out($s);
  472. }
  473. }
  474. }