3 // FPDI - Version 1.4.4
\r
5 // Copyright 2004-2013 Setasign - Jan Slabon
\r
7 // Licensed under the Apache License, Version 2.0 (the "License");
\r
8 // you may not use this file except in compliance with the License.
\r
9 // You may obtain a copy of the License at
\r
11 // http://www.apache.org/licenses/LICENSE-2.0
\r
13 // Unless required by applicable law or agreed to in writing, software
\r
14 // distributed under the License is distributed on an "AS IS" BASIS,
\r
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 // See the License for the specific language governing permissions and
\r
17 // limitations under the License.
\r
20 define('FPDI_VERSION', '1.4.4');
\r
22 // Check for TCPDF and remap TCPDF to FPDF
\r
23 if (class_exists('TCPDF', false)) {
\r
24 require_once('fpdi2tcpdf_bridge.php');
\r
27 require_once('fpdf_tpl.php');
\r
28 require_once('fpdi_pdf_parser.php');
\r
31 class FPDI extends FPDF_TPL {
\r
36 var $current_filename;
\r
48 var $current_parser;
\r
60 var $_don_obj_stack;
\r
63 * Current Object Id.
\r
66 var $_current_obj_id;
\r
69 * The name of the last imported page box
\r
72 var $lastUsedPageBox;
\r
75 * Cache for imported pages/template ids
\r
78 var $_importedPages = array();
\r
83 * @param string $filename a valid filename
\r
84 * @return int number of available pages
\r
86 function setSourceFile($filename) {
\r
87 $this->current_filename = $filename;
\r
89 if (!isset($this->parsers[$filename]))
\r
90 $this->parsers[$filename] = $this->_getPdfParser($filename);
\r
91 $this->current_parser =& $this->parsers[$filename];
\r
93 return $this->parsers[$filename]->getPageCount();
\r
97 * Returns a PDF parser object
\r
99 * @param string $filename
\r
100 * @return fpdi_pdf_parser
\r
102 function _getPdfParser($filename) {
\r
103 return new fpdi_pdf_parser($filename, $this);
\r
107 * Get the current PDF version
\r
111 function getPDFVersion() {
\r
112 return $this->PDFVersion;
\r
116 * Set the PDF version
\r
120 function setPDFVersion($version = '1.3') {
\r
121 $this->PDFVersion = $version;
\r
127 * @param int $pageno pagenumber
\r
128 * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
\r
130 function importPage($pageno, $boxName = '/CropBox') {
\r
131 if ($this->_intpl) {
\r
132 return $this->error('Please import the desired pages before creating a new template.');
\r
135 $fn = $this->current_filename;
\r
137 // check if page already imported
\r
138 $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
\r
139 if (isset($this->_importedPages[$pageKey]))
\r
140 return $this->_importedPages[$pageKey];
\r
142 $parser =& $this->parsers[$fn];
\r
143 $parser->setPageno($pageno);
\r
145 if (!in_array($boxName, $parser->availableBoxes))
\r
146 return $this->Error(sprintf('Unknown box: %s', $boxName));
\r
148 $pageboxes = $parser->getPageBoxes($pageno, $this->k);
\r
152 * CropBox: Default -> MediaBox
\r
153 * BleedBox: Default -> CropBox
\r
154 * TrimBox: Default -> CropBox
\r
155 * ArtBox: Default -> CropBox
\r
157 if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
\r
158 $boxName = '/CropBox';
\r
159 if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
\r
160 $boxName = '/MediaBox';
\r
162 if (!isset($pageboxes[$boxName]))
\r
165 $this->lastUsedPageBox = $boxName;
\r
167 $box = $pageboxes[$boxName];
\r
170 $this->tpls[$this->tpl] = array();
\r
171 $tpl =& $this->tpls[$this->tpl];
\r
172 $tpl['parser'] =& $parser;
\r
173 $tpl['resources'] = $parser->getPageResources();
\r
174 $tpl['buffer'] = $parser->getContent();
\r
175 $tpl['box'] = $box;
\r
177 // To build an array that can be used by PDF_TPL::useTemplate()
\r
178 $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
\r
180 // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
\r
184 // handle rotated pages
\r
185 $rotation = $parser->getPageRotation($pageno);
\r
186 $tpl['_rotationAngle'] = 0;
\r
187 if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
\r
188 $steps = $angle / 90;
\r
192 $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
\r
193 $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
\r
198 $tpl['_rotationAngle'] = $angle * -1;
\r
201 $this->_importedPages[$pageKey] = $this->tpl;
\r
207 * Returns the last used page box
\r
211 function getLastUsedPageBox() {
\r
212 return $this->lastUsedPageBox;
\r
216 function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
\r
217 if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
\r
218 $size = $this->getTemplateSize($tplidx, $_w, $_h);
\r
219 $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
\r
220 $size = array($size['w'], $size['h']);
\r
222 if (is_subclass_of($this, 'TCPDF')) {
\r
223 $this->setPageFormat($size, $orientation);
\r
225 $size = $this->_getpagesize($size);
\r
227 if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
\r
229 // New size or orientation
\r
230 if($orientation=='P')
\r
232 $this->w = $size[0];
\r
233 $this->h = $size[1];
\r
237 $this->w = $size[1];
\r
238 $this->h = $size[0];
\r
240 $this->wPt = $this->w*$this->k;
\r
241 $this->hPt = $this->h*$this->k;
\r
242 $this->PageBreakTrigger = $this->h-$this->bMargin;
\r
243 $this->CurOrientation = $orientation;
\r
244 $this->CurPageSize = $size;
\r
245 $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
\r
250 $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
\r
251 $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
\r
258 * Private method, that rebuilds all needed objects of source files
\r
260 function _putimportedobjects() {
\r
261 if (is_array($this->parsers) && count($this->parsers) > 0) {
\r
262 foreach($this->parsers AS $filename => $p) {
\r
263 $this->current_parser =& $this->parsers[$filename];
\r
264 if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
\r
265 while(($n = key($this->_obj_stack[$filename])) !== null) {
\r
266 $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c, $this->_obj_stack[$filename][$n][1]);
\r
268 $this->_newobj($this->_obj_stack[$filename][$n][0]);
\r
270 if ($nObj[0] == PDF_TYPE_STREAM) {
\r
271 $this->pdf_write_value($nObj);
\r
273 $this->pdf_write_value($nObj[1]);
\r
276 $this->_out('endobj');
\r
277 $this->_obj_stack[$filename][$n] = null; // free memory
\r
278 unset($this->_obj_stack[$filename][$n]);
\r
279 reset($this->_obj_stack[$filename]);
\r
288 * Private Method that writes the form xobjects
\r
290 function _putformxobjects() {
\r
291 $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
\r
292 reset($this->tpls);
\r
293 foreach($this->tpls AS $tplidx => $tpl) {
\r
294 $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
\r
296 $cN = $this->n; // TCPDF/Protection: rem current "n"
\r
298 $this->tpls[$tplidx]['n'] = $this->n;
\r
299 $this->_out('<<' . $filter . '/Type /XObject');
\r
300 $this->_out('/Subtype /Form');
\r
301 $this->_out('/FormType 1');
\r
303 $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
\r
304 (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
\r
305 (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
\r
306 (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
\r
307 (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
\r
315 if (isset($tpl['box'])) {
\r
316 $tx = -$tpl['box']['llx'];
\r
317 $ty = -$tpl['box']['lly'];
\r
319 if ($tpl['_rotationAngle'] <> 0) {
\r
320 $angle = $tpl['_rotationAngle'] * M_PI/180;
\r
324 switch($tpl['_rotationAngle']) {
\r
326 $tx = -$tpl['box']['lly'];
\r
327 $ty = $tpl['box']['urx'];
\r
330 $tx = $tpl['box']['urx'];
\r
331 $ty = $tpl['box']['ury'];
\r
334 $tx = $tpl['box']['ury'];
\r
335 $ty = -$tpl['box']['llx'];
\r
339 } elseif ($tpl['x'] != 0 || $tpl['y'] != 0) {
\r
340 $tx = -$tpl['x'] * 2;
\r
341 $ty = $tpl['y'] * 2;
\r
347 if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
\r
348 $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
\r
349 $c, $s, -$s, $c, $tx, $ty
\r
353 $this->_out('/Resources ');
\r
355 if (isset($tpl['resources'])) {
\r
356 $this->current_parser =& $tpl['parser'];
\r
357 $this->pdf_write_value($tpl['resources']); // "n" will be changed
\r
359 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
\r
360 if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
\r
361 $this->_out('/Font <<');
\r
362 foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
\r
363 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
\r
366 if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
\r
367 isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
\r
369 $this->_out('/XObject <<');
\r
370 if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
\r
371 foreach($this->_res['tpl'][$tplidx]['images'] as $image)
\r
372 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
\r
374 if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
\r
375 foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
\r
376 $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
\r
383 $this->_out('/Group <</Type/Group/S/Transparency>>');
\r
385 $nN = $this->n; // TCPDF: rem new "n"
\r
386 $this->n = $cN; // TCPDF: reset to current "n"
\r
387 if (is_subclass_of($this, 'TCPDF')) {
\r
388 $p = $this->_getrawstream($p);
\r
389 $this->_out('/Length ' . strlen($p) . ' >>');
\r
390 $this->_out("stream\n" . $p . "\nendstream");
\r
392 $this->_out('/Length ' . strlen($p) . ' >>');
\r
393 $this->_putstream($p);
\r
395 $this->_out('endobj');
\r
396 $this->n = $nN; // TCPDF: reset to new "n"
\r
399 $this->_putimportedobjects();
\r
403 * Rewritten to handle existing own defined objects
\r
405 function _newobj($obj_id = false, $onlynewobj = false) {
\r
407 $obj_id = ++$this->n;
\r
410 //Begin a new object
\r
411 if (!$onlynewobj) {
\r
412 $this->offsets[$obj_id] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
\r
413 $this->_out($obj_id . ' 0 obj');
\r
414 $this->_current_obj_id = $obj_id; // for later use with encryption
\r
422 * Needed to rebuild the source document
\r
424 * @param mixed $value A PDF-Value. Structure of values see cases in this method
\r
426 function pdf_write_value(&$value)
\r
428 if (is_subclass_of($this, 'TCPDF')) {
\r
429 parent::pdf_write_value($value);
\r
432 switch ($value[0]) {
\r
434 case PDF_TYPE_TOKEN:
\r
435 $this->_straightOut($value[1] . ' ');
\r
437 case PDF_TYPE_NUMERIC:
\r
438 case PDF_TYPE_REAL:
\r
439 if (is_float($value[1]) && $value[1] != 0) {
\r
440 $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
\r
442 $this->_straightOut($value[1] . ' ');
\r
446 case PDF_TYPE_ARRAY:
\r
448 // An array. Output the proper
\r
449 // structure and move on.
\r
451 $this->_straightOut('[');
\r
452 for ($i = 0; $i < count($value[1]); $i++) {
\r
453 $this->pdf_write_value($value[1][$i]);
\r
459 case PDF_TYPE_DICTIONARY:
\r
462 $this->_straightOut('<<');
\r
466 while (list($k, $v) = each($value[1])) {
\r
467 $this->_straightOut($k . ' ');
\r
468 $this->pdf_write_value($v);
\r
471 $this->_straightOut('>>');
\r
474 case PDF_TYPE_OBJREF:
\r
476 // An indirect object reference
\r
477 // Fill the object stack if needed
\r
478 $cpfn =& $this->current_parser->filename;
\r
480 if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
\r
481 $this->_newobj(false, true);
\r
482 $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
\r
483 $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
\r
485 $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
\r
487 $this->_out($objid . ' 0 R');
\r
490 case PDF_TYPE_STRING:
\r
493 $this->_straightOut('(' . $value[1] . ')');
\r
497 case PDF_TYPE_STREAM:
\r
499 // A stream. First, output the
\r
500 // stream dictionary, then the
\r
501 // stream data itself.
\r
502 $this->pdf_write_value($value[1]);
\r
503 $this->_out('stream');
\r
504 $this->_out($value[2][1]);
\r
505 $this->_out('endstream');
\r
509 $this->_straightOut('<' . $value[1] . '>');
\r
512 case PDF_TYPE_BOOLEAN:
\r
513 $this->_straightOut($value[1] ? 'true ' : 'false ');
\r
516 case PDF_TYPE_NULL:
\r
517 // The null object.
\r
519 $this->_straightOut('null ');
\r
526 * Modified so not each call will add a newline to the output.
\r
528 function _straightOut($s) {
\r
529 if (!is_subclass_of($this, 'TCPDF')) {
\r
530 if($this->state==2)
\r
531 $this->pages[$this->page] .= $s;
\r
533 $this->buffer .= $s;
\r
535 if ($this->state == 2) {
\r
536 if ($this->inxobj) {
\r
537 // we are inside an XObject template
\r
538 $this->xobjects[$this->xobjid]['outdata'] .= $s;
\r
539 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
\r
540 // puts data before page footer
\r
541 $pagebuff = $this->getPageBuffer($this->page);
\r
542 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
\r
543 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
\r
544 $this->setPageBuffer($this->page, $page.$s.$footer);
\r
545 // update footer position
\r
546 $this->footerpos[$this->page] += strlen($s);
\r
549 $this->setPageBuffer($this->page, $s, true);
\r
551 } elseif ($this->state > 0) {
\r
552 // set general data
\r
553 $this->setBuffer($s);
\r
559 * rewritten to close opened parsers
\r
562 function _enddoc() {
\r
564 $this->_closeParsers();
\r
568 * close all files opened by parsers
\r
570 function _closeParsers() {
\r
571 if ($this->state > 2 && count($this->parsers) > 0) {
\r
579 * Removes cylced references and closes the file handles of the parser objects
\r
581 function cleanUp() {
\r
582 foreach ($this->parsers as $k => $_){
\r
583 $this->parsers[$k]->cleanUp();
\r
584 $this->parsers[$k] = null;
\r
585 unset($this->parsers[$k]);
\r