fpdi.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. <?php
  2. //
  3. // FPDI - Version 1.4.2
  4. //
  5. // Copyright 2004-2011 Setasign - Jan Slabon
  6. //
  7. // Licensed under the Apache License, Version 2.0 (the "License");
  8. // you may not use this file except in compliance with the License.
  9. // You may obtain a copy of the License at
  10. //
  11. // http://www.apache.org/licenses/LICENSE-2.0
  12. //
  13. // Unless required by applicable law or agreed to in writing, software
  14. // distributed under the License is distributed on an "AS IS" BASIS,
  15. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. // See the License for the specific language governing permissions and
  17. // limitations under the License.
  18. //
  19. define('FPDI_VERSION', '1.4.2');
  20. // Check for TCPDF and remap TCPDF to FPDF
  21. if (class_exists('TCPDF', false)) {
  22. require_once('fpdi2tcpdf_bridge.php');
  23. }
  24. require_once('fpdf_tpl.php');
  25. require_once('fpdi_pdf_parser.php');
  26. class FPDI extends FPDF_TPL {
  27. /**
  28. * Actual filename
  29. * @var string
  30. */
  31. var $current_filename;
  32. /**
  33. * Parser-Objects
  34. * @var array
  35. */
  36. var $parsers;
  37. /**
  38. * Current parser
  39. * @var object
  40. */
  41. var $current_parser;
  42. /**
  43. * object stack
  44. * @var array
  45. */
  46. var $_obj_stack;
  47. /**
  48. * done object stack
  49. * @var array
  50. */
  51. var $_don_obj_stack;
  52. /**
  53. * Current Object Id.
  54. * @var integer
  55. */
  56. var $_current_obj_id;
  57. /**
  58. * The name of the last imported page box
  59. * @var string
  60. */
  61. var $lastUsedPageBox;
  62. /**
  63. * Cache for imported pages/template ids
  64. * @var array
  65. */
  66. var $_importedPages = array();
  67. /**
  68. * Set a source-file
  69. *
  70. * @param string $filename a valid filename
  71. * @return int number of available pages
  72. */
  73. function setSourceFile($filename) {
  74. $this->current_filename = $filename;
  75. if (!isset($this->parsers[$filename]))
  76. $this->parsers[$filename] = $this->_getPdfParser($filename);
  77. $this->current_parser =& $this->parsers[$filename];
  78. return $this->parsers[$filename]->getPageCount();
  79. }
  80. /**
  81. * Returns a PDF parser object
  82. *
  83. * @param string $filename
  84. * @return fpdi_pdf_parser
  85. */
  86. function _getPdfParser($filename) {
  87. return new fpdi_pdf_parser($filename, $this);
  88. }
  89. /**
  90. * Get the current PDF version
  91. *
  92. * @return string
  93. */
  94. function getPDFVersion() {
  95. return $this->PDFVersion;
  96. }
  97. /**
  98. * Set the PDF version
  99. *
  100. * @return string
  101. */
  102. function setPDFVersion($version = '1.3') {
  103. $this->PDFVersion = $version;
  104. }
  105. /**
  106. * Import a page
  107. *
  108. * @param int $pageno pagenumber
  109. * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
  110. */
  111. function importPage($pageno, $boxName = '/CropBox') {
  112. if ($this->_intpl) {
  113. return $this->error('Please import the desired pages before creating a new template.');
  114. }
  115. $fn = $this->current_filename;
  116. // check if page already imported
  117. $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
  118. if (isset($this->_importedPages[$pageKey]))
  119. return $this->_importedPages[$pageKey];
  120. $parser =& $this->parsers[$fn];
  121. $parser->setPageno($pageno);
  122. if (!in_array($boxName, $parser->availableBoxes))
  123. return $this->Error(sprintf('Unknown box: %s', $boxName));
  124. $pageboxes = $parser->getPageBoxes($pageno, $this->k);
  125. /**
  126. * MediaBox
  127. * CropBox: Default -> MediaBox
  128. * BleedBox: Default -> CropBox
  129. * TrimBox: Default -> CropBox
  130. * ArtBox: Default -> CropBox
  131. */
  132. if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
  133. $boxName = '/CropBox';
  134. if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
  135. $boxName = '/MediaBox';
  136. if (!isset($pageboxes[$boxName]))
  137. return false;
  138. $this->lastUsedPageBox = $boxName;
  139. $box = $pageboxes[$boxName];
  140. $this->tpl++;
  141. $this->tpls[$this->tpl] = array();
  142. $tpl =& $this->tpls[$this->tpl];
  143. $tpl['parser'] =& $parser;
  144. $tpl['resources'] = $parser->getPageResources();
  145. $tpl['buffer'] = $parser->getContent();
  146. $tpl['box'] = $box;
  147. // To build an array that can be used by PDF_TPL::useTemplate()
  148. $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
  149. // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
  150. $tpl['x'] = 0;
  151. $tpl['y'] = 0;
  152. // handle rotated pages
  153. $rotation = $parser->getPageRotation($pageno);
  154. $tpl['_rotationAngle'] = 0;
  155. if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
  156. $steps = $angle / 90;
  157. $_w = $tpl['w'];
  158. $_h = $tpl['h'];
  159. $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
  160. $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
  161. if ($angle < 0)
  162. $angle += 360;
  163. $tpl['_rotationAngle'] = $angle * -1;
  164. }
  165. $this->_importedPages[$pageKey] = $this->tpl;
  166. return $this->tpl;
  167. }
  168. /**
  169. * Returns the last used page box
  170. *
  171. * @return string
  172. */
  173. function getLastUsedPageBox() {
  174. return $this->lastUsedPageBox;
  175. }
  176. function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
  177. if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
  178. $size = $this->getTemplateSize($tplidx, $_w, $_h);
  179. $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
  180. $size = array($size['w'], $size['h']);
  181. if (is_subclass_of($this, 'TCPDF')) {
  182. $this->setPageFormat($size, $orientation);
  183. } else {
  184. $size = $this->_getpagesize($size);
  185. if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
  186. {
  187. // New size or orientation
  188. if($orientation=='P')
  189. {
  190. $this->w = $size[0];
  191. $this->h = $size[1];
  192. }
  193. else
  194. {
  195. $this->w = $size[1];
  196. $this->h = $size[0];
  197. }
  198. $this->wPt = $this->w*$this->k;
  199. $this->hPt = $this->h*$this->k;
  200. $this->PageBreakTrigger = $this->h-$this->bMargin;
  201. $this->CurOrientation = $orientation;
  202. $this->CurPageSize = $size;
  203. $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
  204. }
  205. }
  206. }
  207. $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
  208. $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
  209. $this->_out('Q');
  210. return $s;
  211. }
  212. /**
  213. * Private method, that rebuilds all needed objects of source files
  214. */
  215. function _putimportedobjects() {
  216. if (is_array($this->parsers) && count($this->parsers) > 0) {
  217. foreach($this->parsers AS $filename => $p) {
  218. $this->current_parser =& $this->parsers[$filename];
  219. if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
  220. while(($n = key($this->_obj_stack[$filename])) !== null) {
  221. $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c, $this->_obj_stack[$filename][$n][1]);
  222. $this->_newobj($this->_obj_stack[$filename][$n][0]);
  223. if ($nObj[0] == PDF_TYPE_STREAM) {
  224. $this->pdf_write_value($nObj);
  225. } else {
  226. $this->pdf_write_value($nObj[1]);
  227. }
  228. $this->_out('endobj');
  229. $this->_obj_stack[$filename][$n] = null; // free memory
  230. unset($this->_obj_stack[$filename][$n]);
  231. reset($this->_obj_stack[$filename]);
  232. }
  233. }
  234. }
  235. }
  236. }
  237. /**
  238. * Private Method that writes the form xobjects
  239. */
  240. function _putformxobjects() {
  241. $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
  242. reset($this->tpls);
  243. foreach($this->tpls AS $tplidx => $tpl) {
  244. $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
  245. $this->_newobj();
  246. $cN = $this->n; // TCPDF/Protection: rem current "n"
  247. $this->tpls[$tplidx]['n'] = $this->n;
  248. $this->_out('<<' . $filter . '/Type /XObject');
  249. $this->_out('/Subtype /Form');
  250. $this->_out('/FormType 1');
  251. $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
  252. (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
  253. (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
  254. (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
  255. (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
  256. ));
  257. $c = 1;
  258. $s = 0;
  259. $tx = 0;
  260. $ty = 0;
  261. if (isset($tpl['box'])) {
  262. $tx = -$tpl['box']['llx'];
  263. $ty = -$tpl['box']['lly'];
  264. if ($tpl['_rotationAngle'] <> 0) {
  265. $angle = $tpl['_rotationAngle'] * M_PI/180;
  266. $c=cos($angle);
  267. $s=sin($angle);
  268. switch($tpl['_rotationAngle']) {
  269. case -90:
  270. $tx = -$tpl['box']['lly'];
  271. $ty = $tpl['box']['urx'];
  272. break;
  273. case -180:
  274. $tx = $tpl['box']['urx'];
  275. $ty = $tpl['box']['ury'];
  276. break;
  277. case -270:
  278. $tx = $tpl['box']['ury'];
  279. $ty = -$tpl['box']['llx'];
  280. break;
  281. }
  282. }
  283. } else if ($tpl['x'] != 0 || $tpl['y'] != 0) {
  284. $tx = -$tpl['x'] * 2;
  285. $ty = $tpl['y'] * 2;
  286. }
  287. $tx *= $this->k;
  288. $ty *= $this->k;
  289. if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
  290. $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
  291. $c, $s, -$s, $c, $tx, $ty
  292. ));
  293. }
  294. $this->_out('/Resources ');
  295. if (isset($tpl['resources'])) {
  296. $this->current_parser =& $tpl['parser'];
  297. $this->pdf_write_value($tpl['resources']); // "n" will be changed
  298. } else {
  299. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  300. if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
  301. $this->_out('/Font <<');
  302. foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
  303. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  304. $this->_out('>>');
  305. }
  306. if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
  307. isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
  308. {
  309. $this->_out('/XObject <<');
  310. if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
  311. foreach($this->_res['tpl'][$tplidx]['images'] as $image)
  312. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  313. }
  314. if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
  315. foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
  316. $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
  317. }
  318. $this->_out('>>');
  319. }
  320. $this->_out('>>');
  321. }
  322. $nN = $this->n; // TCPDF: rem new "n"
  323. $this->n = $cN; // TCPDF: reset to current "n"
  324. if (is_subclass_of($this, 'TCPDF')) {
  325. $p = $this->_getrawstream($p);
  326. $this->_out('/Length ' . strlen($p) . ' >>');
  327. $this->_out("stream\n" . $p . "\nendstream");
  328. } else {
  329. $this->_out('/Length ' . strlen($p) . ' >>');
  330. $this->_putstream($p);
  331. }
  332. $this->_out('endobj');
  333. $this->n = $nN; // TCPDF: reset to new "n"
  334. }
  335. $this->_putimportedobjects();
  336. }
  337. /**
  338. * Rewritten to handle existing own defined objects
  339. */
  340. function _newobj($obj_id = false, $onlynewobj = false) {
  341. if (!$obj_id) {
  342. $obj_id = ++$this->n;
  343. }
  344. //Begin a new object
  345. if (!$onlynewobj) {
  346. $this->offsets[$obj_id] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
  347. $this->_out($obj_id . ' 0 obj');
  348. $this->_current_obj_id = $obj_id; // for later use with encryption
  349. }
  350. return $obj_id;
  351. }
  352. /**
  353. * Writes a value
  354. * Needed to rebuild the source document
  355. *
  356. * @param mixed $value A PDF-Value. Structure of values see cases in this method
  357. */
  358. function pdf_write_value(&$value)
  359. {
  360. if (is_subclass_of($this, 'TCPDF')) {
  361. parent::pdf_write_value($value);
  362. }
  363. switch ($value[0]) {
  364. case PDF_TYPE_TOKEN:
  365. $this->_straightOut($value[1] . ' ');
  366. break;
  367. case PDF_TYPE_NUMERIC:
  368. case PDF_TYPE_REAL:
  369. if (is_float($value[1]) && $value[1] != 0) {
  370. $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
  371. } else {
  372. $this->_straightOut($value[1] . ' ');
  373. }
  374. break;
  375. case PDF_TYPE_ARRAY:
  376. // An array. Output the proper
  377. // structure and move on.
  378. $this->_straightOut('[');
  379. for ($i = 0; $i < count($value[1]); $i++) {
  380. $this->pdf_write_value($value[1][$i]);
  381. }
  382. $this->_out(']');
  383. break;
  384. case PDF_TYPE_DICTIONARY:
  385. // A dictionary.
  386. $this->_straightOut('<<');
  387. reset ($value[1]);
  388. while (list($k, $v) = each($value[1])) {
  389. $this->_straightOut($k . ' ');
  390. $this->pdf_write_value($v);
  391. }
  392. $this->_straightOut('>>');
  393. break;
  394. case PDF_TYPE_OBJREF:
  395. // An indirect object reference
  396. // Fill the object stack if needed
  397. $cpfn =& $this->current_parser->filename;
  398. if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
  399. $this->_newobj(false, true);
  400. $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
  401. $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
  402. }
  403. $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
  404. $this->_out($objid . ' 0 R');
  405. break;
  406. case PDF_TYPE_STRING:
  407. // A string.
  408. $this->_straightOut('(' . $value[1] . ')');
  409. break;
  410. case PDF_TYPE_STREAM:
  411. // A stream. First, output the
  412. // stream dictionary, then the
  413. // stream data itself.
  414. $this->pdf_write_value($value[1]);
  415. $this->_out('stream');
  416. $this->_out($value[2][1]);
  417. $this->_out('endstream');
  418. break;
  419. case PDF_TYPE_HEX:
  420. $this->_straightOut('<' . $value[1] . '>');
  421. break;
  422. case PDF_TYPE_BOOLEAN:
  423. $this->_straightOut($value[1] ? 'true ' : 'false ');
  424. break;
  425. case PDF_TYPE_NULL:
  426. // The null object.
  427. $this->_straightOut('null ');
  428. break;
  429. }
  430. }
  431. /**
  432. * Modified so not each call will add a newline to the output.
  433. */
  434. function _straightOut($s) {
  435. if (!is_subclass_of($this, 'TCPDF')) {
  436. if($this->state==2)
  437. $this->pages[$this->page] .= $s;
  438. else
  439. $this->buffer .= $s;
  440. } else {
  441. if ($this->state == 2) {
  442. if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
  443. // puts data before page footer
  444. $page = substr($this->getPageBuffer($this->page), 0, -$this->footerlen[$this->page]);
  445. $footer = substr($this->getPageBuffer($this->page), -$this->footerlen[$this->page]);
  446. $this->setPageBuffer($this->page, $page . ' ' . $s . "\n" . $footer);
  447. } else {
  448. $this->setPageBuffer($this->page, $s, true);
  449. }
  450. } else {
  451. $this->setBuffer($s);
  452. }
  453. }
  454. }
  455. /**
  456. * rewritten to close opened parsers
  457. *
  458. */
  459. function _enddoc() {
  460. parent::_enddoc();
  461. $this->_closeParsers();
  462. }
  463. /**
  464. * close all files opened by parsers
  465. */
  466. function _closeParsers() {
  467. if ($this->state > 2 && count($this->parsers) > 0) {
  468. foreach ($this->parsers as $k => $_){
  469. $this->parsers[$k]->closeFile();
  470. $this->parsers[$k] = null;
  471. unset($this->parsers[$k]);
  472. }
  473. return true;
  474. }
  475. return false;
  476. }
  477. }