3 // FPDI - Version 1.4.2
5 // Copyright 2004-2011 Setasign - Jan Slabon
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
20 define('FPDI_VERSION', '1.4.2');
22 // Check for TCPDF and remap TCPDF to FPDF
23 if (class_exists('TCPDF', false)) {
24 require_once('fpdi2tcpdf_bridge.php');
27 require_once('fpdf_tpl.php');
28 require_once('fpdi_pdf_parser.php');
31 class FPDI
extends FPDF_TPL
{
36 var $current_filename;
69 * The name of the last imported page box
75 * Cache for imported pages/template ids
78 var $_importedPages = array();
83 * @param string $filename a valid filename
84 * @return int number of available pages
86 function setSourceFile($filename) {
87 $this->current_filename
= $filename;
89 if (!isset($this->parsers
[$filename]))
90 $this->parsers
[$filename] = $this->_getPdfParser($filename);
91 $this->current_parser
=& $this->parsers
[$filename];
93 return $this->parsers
[$filename]->getPageCount();
97 * Returns a PDF parser object
99 * @param string $filename
100 * @return fpdi_pdf_parser
102 function _getPdfParser($filename) {
103 return new fpdi_pdf_parser($filename, $this);
107 * Get the current PDF version
111 function getPDFVersion() {
112 return $this->PDFVersion
;
116 * Set the PDF version
120 function setPDFVersion($version = '1.3') {
121 $this->PDFVersion
= $version;
127 * @param int $pageno pagenumber
128 * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
130 function importPage($pageno, $boxName = '/CropBox') {
132 return $this->error('Please import the desired pages before creating a new template.');
135 $fn = $this->current_filename
;
137 // check if page already imported
138 $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
139 if (isset($this->_importedPages
[$pageKey]))
140 return $this->_importedPages
[$pageKey];
142 $parser =& $this->parsers
[$fn];
143 $parser->setPageno($pageno);
145 if (!in_array($boxName, $parser->availableBoxes
))
146 return $this->Error(sprintf('Unknown box: %s', $boxName));
148 $pageboxes = $parser->getPageBoxes($pageno, $this->k
);
152 * CropBox: Default -> MediaBox
153 * BleedBox: Default -> CropBox
154 * TrimBox: Default -> CropBox
155 * ArtBox: Default -> CropBox
157 if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' ||
$boxName == '/TrimBox' ||
$boxName == '/ArtBox'))
158 $boxName = '/CropBox';
159 if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
160 $boxName = '/MediaBox';
162 if (!isset($pageboxes[$boxName]))
165 $this->lastUsedPageBox
= $boxName;
167 $box = $pageboxes[$boxName];
170 $this->tpls
[$this->tpl
] = array();
171 $tpl =& $this->tpls
[$this->tpl
];
172 $tpl['parser'] =& $parser;
173 $tpl['resources'] = $parser->getPageResources();
174 $tpl['buffer'] = $parser->getContent();
177 // To build an array that can be used by PDF_TPL::useTemplate()
178 $this->tpls
[$this->tpl
] = array_merge($this->tpls
[$this->tpl
], $box);
180 // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
184 // handle rotated pages
185 $rotation = $parser->getPageRotation($pageno);
186 $tpl['_rotationAngle'] = 0;
187 if (isset($rotation[1]) && ($angle = $rotation[1] %
360) != 0) {
188 $steps = $angle / 90;
192 $tpl['w'] = $steps %
2 == 0 ?
$_w : $_h;
193 $tpl['h'] = $steps %
2 == 0 ?
$_h : $_w;
198 $tpl['_rotationAngle'] = $angle * -1;
201 $this->_importedPages
[$pageKey] = $this->tpl
;
207 * Returns the last used page box
211 function getLastUsedPageBox() {
212 return $this->lastUsedPageBox
;
216 function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
217 if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
218 $size = $this->getTemplateSize($tplidx, $_w, $_h);
219 $orientation = $size['w'] > $size['h'] ?
'L' : 'P';
220 $size = array($size['w'], $size['h']);
222 if (is_subclass_of($this, 'TCPDF')) {
223 $this->setPageFormat($size, $orientation);
225 $size = $this->_getpagesize($size);
227 if($orientation!=$this->CurOrientation ||
$size[0]!=$this->CurPageSize
[0] ||
$size[1]!=$this->CurPageSize
[1])
229 // New size or orientation
230 if($orientation=='P')
240 $this->wPt
= $this->w
*$this->k
;
241 $this->hPt
= $this->h
*$this->k
;
242 $this->PageBreakTrigger
= $this->h
-$this->bMargin
;
243 $this->CurOrientation
= $orientation;
244 $this->CurPageSize
= $size;
245 $this->PageSizes
[$this->page
] = array($this->wPt
, $this->hPt
);
250 $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
251 $s = parent
::useTemplate($tplidx, $_x, $_y, $_w, $_h);
258 * Private method, that rebuilds all needed objects of source files
260 function _putimportedobjects() {
261 if (is_array($this->parsers
) && count($this->parsers
) > 0) {
262 foreach($this->parsers
AS $filename => $p) {
263 $this->current_parser
=& $this->parsers
[$filename];
264 if (isset($this->_obj_stack
[$filename]) && is_array($this->_obj_stack
[$filename])) {
265 while(($n = key($this->_obj_stack
[$filename])) !== null) {
266 $nObj = $this->current_parser
->pdf_resolve_object($this->current_parser
->c
, $this->_obj_stack
[$filename][$n][1]);
268 $this->_newobj($this->_obj_stack
[$filename][$n][0]);
270 if ($nObj[0] == PDF_TYPE_STREAM
) {
271 $this->pdf_write_value($nObj);
273 $this->pdf_write_value($nObj[1]);
276 $this->_out('endobj');
277 $this->_obj_stack
[$filename][$n] = null; // free memory
278 unset($this->_obj_stack
[$filename][$n]);
279 reset($this->_obj_stack
[$filename]);
288 * Private Method that writes the form xobjects
290 function _putformxobjects() {
291 $filter=($this->compress
) ?
'/Filter /FlateDecode ' : '';
293 foreach($this->tpls
AS $tplidx => $tpl) {
294 $p=($this->compress
) ?
gzcompress($tpl['buffer']) : $tpl['buffer'];
296 $cN = $this->n
; // TCPDF/Protection: rem current "n"
298 $this->tpls
[$tplidx]['n'] = $this->n
;
299 $this->_out('<<' . $filter . '/Type /XObject');
300 $this->_out('/Subtype /Form');
301 $this->_out('/FormType 1');
303 $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
304 (isset($tpl['box']['llx']) ?
$tpl['box']['llx'] : $tpl['x']) * $this->k
,
305 (isset($tpl['box']['lly']) ?
$tpl['box']['lly'] : -$tpl['y']) * $this->k
,
306 (isset($tpl['box']['urx']) ?
$tpl['box']['urx'] : $tpl['w'] +
$tpl['x']) * $this->k
,
307 (isset($tpl['box']['ury']) ?
$tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
315 if (isset($tpl['box'])) {
316 $tx = -$tpl['box']['llx'];
317 $ty = -$tpl['box']['lly'];
319 if ($tpl['_rotationAngle'] <> 0) {
320 $angle = $tpl['_rotationAngle'] * M_PI
/180;
324 switch($tpl['_rotationAngle']) {
326 $tx = -$tpl['box']['lly'];
327 $ty = $tpl['box']['urx'];
330 $tx = $tpl['box']['urx'];
331 $ty = $tpl['box']['ury'];
334 $tx = $tpl['box']['ury'];
335 $ty = -$tpl['box']['llx'];
339 } else if ($tpl['x'] != 0 ||
$tpl['y'] != 0) {
340 $tx = -$tpl['x'] * 2;
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
353 $this->_out('/Resources ');
355 if (isset($tpl['resources'])) {
356 $this->current_parser
=& $tpl['parser'];
357 $this->pdf_write_value($tpl['resources']); // "n" will be changed
359 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
360 if (isset($this->_res
['tpl'][$tplidx]['fonts']) && count($this->_res
['tpl'][$tplidx]['fonts'])) {
361 $this->_out('/Font <<');
362 foreach($this->_res
['tpl'][$tplidx]['fonts'] as $font)
363 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
366 if(isset($this->_res
['tpl'][$tplidx]['images']) && count($this->_res
['tpl'][$tplidx]['images']) ||
367 isset($this->_res
['tpl'][$tplidx]['tpls']) && count($this->_res
['tpl'][$tplidx]['tpls']))
369 $this->_out('/XObject <<');
370 if (isset($this->_res
['tpl'][$tplidx]['images']) && count($this->_res
['tpl'][$tplidx]['images'])) {
371 foreach($this->_res
['tpl'][$tplidx]['images'] as $image)
372 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
374 if (isset($this->_res
['tpl'][$tplidx]['tpls']) && count($this->_res
['tpl'][$tplidx]['tpls'])) {
375 foreach($this->_res
['tpl'][$tplidx]['tpls'] as $i => $tpl)
376 $this->_out($this->tplprefix
. $i . ' ' . $tpl['n'] . ' 0 R');
383 $nN = $this->n
; // TCPDF: rem new "n"
384 $this->n
= $cN; // TCPDF: reset to current "n"
385 if (is_subclass_of($this, 'TCPDF')) {
386 $p = $this->_getrawstream($p);
387 $this->_out('/Length ' . strlen($p) . ' >>');
388 $this->_out("stream\n" . $p . "\nendstream");
390 $this->_out('/Length ' . strlen($p) . ' >>');
391 $this->_putstream($p);
393 $this->_out('endobj');
394 $this->n
= $nN; // TCPDF: reset to new "n"
397 $this->_putimportedobjects();
401 * Rewritten to handle existing own defined objects
403 function _newobj($obj_id = false, $onlynewobj = false) {
405 $obj_id = ++
$this->n
;
410 $this->offsets
[$obj_id] = is_subclass_of($this, 'TCPDF') ?
$this->bufferlen
: strlen($this->buffer
);
411 $this->_out($obj_id . ' 0 obj');
412 $this->_current_obj_id
= $obj_id; // for later use with encryption
420 * Needed to rebuild the source document
422 * @param mixed $value A PDF-Value. Structure of values see cases in this method
424 function pdf_write_value(&$value)
426 if (is_subclass_of($this, 'TCPDF')) {
427 parent
::pdf_write_value($value);
433 $this->_straightOut($value[1] . ' ');
435 case PDF_TYPE_NUMERIC
:
437 if (is_float($value[1]) && $value[1] != 0) {
438 $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
440 $this->_straightOut($value[1] . ' ');
446 // An array. Output the proper
447 // structure and move on.
449 $this->_straightOut('[');
450 for ($i = 0; $i < count($value[1]); $i++
) {
451 $this->pdf_write_value($value[1][$i]);
457 case PDF_TYPE_DICTIONARY
:
460 $this->_straightOut('<<');
464 while (list($k, $v) = each($value[1])) {
465 $this->_straightOut($k . ' ');
466 $this->pdf_write_value($v);
469 $this->_straightOut('>>');
472 case PDF_TYPE_OBJREF
:
474 // An indirect object reference
475 // Fill the object stack if needed
476 $cpfn =& $this->current_parser
->filename
;
478 if (!isset($this->_don_obj_stack
[$cpfn][$value[1]])) {
479 $this->_newobj(false, true);
480 $this->_obj_stack
[$cpfn][$value[1]] = array($this->n
, $value);
481 $this->_don_obj_stack
[$cpfn][$value[1]] = array($this->n
, $value); // Value is maybee obsolete!!!
483 $objid = $this->_don_obj_stack
[$cpfn][$value[1]][0];
485 $this->_out($objid . ' 0 R');
488 case PDF_TYPE_STRING
:
491 $this->_straightOut('(' . $value[1] . ')');
495 case PDF_TYPE_STREAM
:
497 // A stream. First, output the
498 // stream dictionary, then the
499 // stream data itself.
500 $this->pdf_write_value($value[1]);
501 $this->_out('stream');
502 $this->_out($value[2][1]);
503 $this->_out('endstream');
507 $this->_straightOut('<' . $value[1] . '>');
510 case PDF_TYPE_BOOLEAN
:
511 $this->_straightOut($value[1] ?
'true ' : 'false ');
517 $this->_straightOut('null ');
524 * Modified so not each call will add a newline to the output.
526 function _straightOut($s) {
527 if (!is_subclass_of($this, 'TCPDF')) {
529 $this->pages
[$this->page
] .= $s;
533 if ($this->state
== 2) {
534 if (isset($this->footerlen
[$this->page
]) AND ($this->footerlen
[$this->page
] > 0)) {
535 // puts data before page footer
536 $page = substr($this->getPageBuffer($this->page
), 0, -$this->footerlen
[$this->page
]);
537 $footer = substr($this->getPageBuffer($this->page
), -$this->footerlen
[$this->page
]);
538 $this->setPageBuffer($this->page
, $page . ' ' . $s . "\n" . $footer);
540 $this->setPageBuffer($this->page
, $s, true);
543 $this->setBuffer($s);
549 * rewritten to close opened parsers
554 $this->_closeParsers();
558 * close all files opened by parsers
560 function _closeParsers() {
561 if ($this->state
> 2 && count($this->parsers
) > 0) {
562 foreach ($this->parsers
as $k => $_){
563 $this->parsers
[$k]->closeFile();
564 $this->parsers
[$k] = null;
565 unset($this->parsers
[$k]);