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