[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/assign/feedback/editpdf/fpdi/ -> fpdi.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1