1*dc4d9dc6SAnna Dabrowska<?php 2*dc4d9dc6SAnna Dabrowska/** 3*dc4d9dc6SAnna Dabrowska * This file is part of FPDI 4*dc4d9dc6SAnna Dabrowska * 5*dc4d9dc6SAnna Dabrowska * @package setasign\Fpdi 6*dc4d9dc6SAnna Dabrowska * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) 7*dc4d9dc6SAnna Dabrowska * @license http://opensource.org/licenses/mit-license The MIT License 8*dc4d9dc6SAnna Dabrowska */ 9*dc4d9dc6SAnna Dabrowska 10*dc4d9dc6SAnna Dabrowskanamespace setasign\Fpdi; 11*dc4d9dc6SAnna Dabrowska 12*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException; 13*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Filter\FilterException; 14*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\PdfParser; 15*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\PdfParserException; 16*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\StreamReader; 17*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfArray; 18*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfBoolean; 19*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfDictionary; 20*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfHexString; 21*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfIndirectObject; 22*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference; 23*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfName; 24*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfNull; 25*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfNumeric; 26*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfStream; 27*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfString; 28*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfToken; 29*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfType; 30*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfTypeException; 31*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfReader\PageBoundaries; 32*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfReader\PdfReader; 33*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfReader\PdfReaderException; 34*dc4d9dc6SAnna Dabrowskause /* This namespace/class is used by the commercial FPDI PDF-Parser add-on. */ 35*dc4d9dc6SAnna Dabrowska /** @noinspection PhpUndefinedClassInspection */ 36*dc4d9dc6SAnna Dabrowska /** @noinspection PhpUndefinedNamespaceInspection */ 37*dc4d9dc6SAnna Dabrowska setasign\FpdiPdfParser\PdfParser\PdfParser as FpdiPdfParser; 38*dc4d9dc6SAnna Dabrowska 39*dc4d9dc6SAnna Dabrowska/** 40*dc4d9dc6SAnna Dabrowska * The FpdiTrait 41*dc4d9dc6SAnna Dabrowska * 42*dc4d9dc6SAnna Dabrowska * This trait offers the core functionalities of FPDI. By passing them to a trait we can reuse it with e.g. TCPDF in a 43*dc4d9dc6SAnna Dabrowska * very easy way. 44*dc4d9dc6SAnna Dabrowska * 45*dc4d9dc6SAnna Dabrowska * @package setasign\Fpdi 46*dc4d9dc6SAnna Dabrowska */ 47*dc4d9dc6SAnna Dabrowskatrait FpdiTrait 48*dc4d9dc6SAnna Dabrowska{ 49*dc4d9dc6SAnna Dabrowska /** 50*dc4d9dc6SAnna Dabrowska * The pdf reader instances. 51*dc4d9dc6SAnna Dabrowska * 52*dc4d9dc6SAnna Dabrowska * @var PdfReader[] 53*dc4d9dc6SAnna Dabrowska */ 54*dc4d9dc6SAnna Dabrowska protected $readers = []; 55*dc4d9dc6SAnna Dabrowska 56*dc4d9dc6SAnna Dabrowska /** 57*dc4d9dc6SAnna Dabrowska * Instances created internally. 58*dc4d9dc6SAnna Dabrowska * 59*dc4d9dc6SAnna Dabrowska * @var array 60*dc4d9dc6SAnna Dabrowska */ 61*dc4d9dc6SAnna Dabrowska protected $createdReaders = []; 62*dc4d9dc6SAnna Dabrowska 63*dc4d9dc6SAnna Dabrowska /** 64*dc4d9dc6SAnna Dabrowska * The current reader id. 65*dc4d9dc6SAnna Dabrowska * 66*dc4d9dc6SAnna Dabrowska * @var string 67*dc4d9dc6SAnna Dabrowska */ 68*dc4d9dc6SAnna Dabrowska protected $currentReaderId; 69*dc4d9dc6SAnna Dabrowska 70*dc4d9dc6SAnna Dabrowska /** 71*dc4d9dc6SAnna Dabrowska * Data of all imported pages. 72*dc4d9dc6SAnna Dabrowska * 73*dc4d9dc6SAnna Dabrowska * @var array 74*dc4d9dc6SAnna Dabrowska */ 75*dc4d9dc6SAnna Dabrowska protected $importedPages = []; 76*dc4d9dc6SAnna Dabrowska 77*dc4d9dc6SAnna Dabrowska /** 78*dc4d9dc6SAnna Dabrowska * A map from object numbers of imported objects to new assigned object numbers by FPDF. 79*dc4d9dc6SAnna Dabrowska * 80*dc4d9dc6SAnna Dabrowska * @var array 81*dc4d9dc6SAnna Dabrowska */ 82*dc4d9dc6SAnna Dabrowska protected $objectMap = []; 83*dc4d9dc6SAnna Dabrowska 84*dc4d9dc6SAnna Dabrowska /** 85*dc4d9dc6SAnna Dabrowska * An array with information about objects, which needs to be copied to the resulting document. 86*dc4d9dc6SAnna Dabrowska * 87*dc4d9dc6SAnna Dabrowska * @var array 88*dc4d9dc6SAnna Dabrowska */ 89*dc4d9dc6SAnna Dabrowska protected $objectsToCopy = []; 90*dc4d9dc6SAnna Dabrowska 91*dc4d9dc6SAnna Dabrowska /** 92*dc4d9dc6SAnna Dabrowska * Release resources and file handles. 93*dc4d9dc6SAnna Dabrowska * 94*dc4d9dc6SAnna Dabrowska * This method is called internally when the document is created successfully. By default it only cleans up 95*dc4d9dc6SAnna Dabrowska * stream reader instances which were created internally. 96*dc4d9dc6SAnna Dabrowska * 97*dc4d9dc6SAnna Dabrowska * @param bool $allReaders 98*dc4d9dc6SAnna Dabrowska */ 99*dc4d9dc6SAnna Dabrowska public function cleanUp($allReaders = false) 100*dc4d9dc6SAnna Dabrowska { 101*dc4d9dc6SAnna Dabrowska $readers = $allReaders ? array_keys($this->readers) : $this->createdReaders; 102*dc4d9dc6SAnna Dabrowska foreach ($readers as $id) { 103*dc4d9dc6SAnna Dabrowska $this->readers[$id]->getParser()->getStreamReader()->cleanUp(); 104*dc4d9dc6SAnna Dabrowska unset($this->readers[$id]); 105*dc4d9dc6SAnna Dabrowska } 106*dc4d9dc6SAnna Dabrowska 107*dc4d9dc6SAnna Dabrowska $this->createdReaders= []; 108*dc4d9dc6SAnna Dabrowska } 109*dc4d9dc6SAnna Dabrowska 110*dc4d9dc6SAnna Dabrowska /** 111*dc4d9dc6SAnna Dabrowska * Set the minimal PDF version. 112*dc4d9dc6SAnna Dabrowska * 113*dc4d9dc6SAnna Dabrowska * @param string $pdfVersion 114*dc4d9dc6SAnna Dabrowska */ 115*dc4d9dc6SAnna Dabrowska protected function setMinPdfVersion($pdfVersion) 116*dc4d9dc6SAnna Dabrowska { 117*dc4d9dc6SAnna Dabrowska if (\version_compare($pdfVersion, $this->PDFVersion, '>')) { 118*dc4d9dc6SAnna Dabrowska $this->PDFVersion = $pdfVersion; 119*dc4d9dc6SAnna Dabrowska } 120*dc4d9dc6SAnna Dabrowska } 121*dc4d9dc6SAnna Dabrowska 122*dc4d9dc6SAnna Dabrowska /** @noinspection PhpUndefinedClassInspection */ 123*dc4d9dc6SAnna Dabrowska /** 124*dc4d9dc6SAnna Dabrowska * Get a new pdf parser instance. 125*dc4d9dc6SAnna Dabrowska * 126*dc4d9dc6SAnna Dabrowska * @param StreamReader $streamReader 127*dc4d9dc6SAnna Dabrowska * @return PdfParser|FpdiPdfParser 128*dc4d9dc6SAnna Dabrowska */ 129*dc4d9dc6SAnna Dabrowska protected function getPdfParserInstance(StreamReader $streamReader) 130*dc4d9dc6SAnna Dabrowska { 131*dc4d9dc6SAnna Dabrowska /** @noinspection PhpUndefinedClassInspection */ 132*dc4d9dc6SAnna Dabrowska if (\class_exists(FpdiPdfParser::class)) { 133*dc4d9dc6SAnna Dabrowska /** @noinspection PhpUndefinedClassInspection */ 134*dc4d9dc6SAnna Dabrowska return new FpdiPdfParser($streamReader); 135*dc4d9dc6SAnna Dabrowska } 136*dc4d9dc6SAnna Dabrowska 137*dc4d9dc6SAnna Dabrowska return new PdfParser($streamReader); 138*dc4d9dc6SAnna Dabrowska } 139*dc4d9dc6SAnna Dabrowska 140*dc4d9dc6SAnna Dabrowska /** 141*dc4d9dc6SAnna Dabrowska * Get an unique reader id by the $file parameter. 142*dc4d9dc6SAnna Dabrowska * 143*dc4d9dc6SAnna Dabrowska * @param string|resource|PdfReader|StreamReader $file An open file descriptor, a path to a file, a PdfReader 144*dc4d9dc6SAnna Dabrowska * instance or a StreamReader instance. 145*dc4d9dc6SAnna Dabrowska * @return string 146*dc4d9dc6SAnna Dabrowska */ 147*dc4d9dc6SAnna Dabrowska protected function getPdfReaderId($file) 148*dc4d9dc6SAnna Dabrowska { 149*dc4d9dc6SAnna Dabrowska if (\is_resource($file)) { 150*dc4d9dc6SAnna Dabrowska $id = (string) $file; 151*dc4d9dc6SAnna Dabrowska } elseif (\is_string($file)) { 152*dc4d9dc6SAnna Dabrowska $id = \realpath($file); 153*dc4d9dc6SAnna Dabrowska if ($id === false) { 154*dc4d9dc6SAnna Dabrowska $id = $file; 155*dc4d9dc6SAnna Dabrowska } 156*dc4d9dc6SAnna Dabrowska } elseif (\is_object($file)) { 157*dc4d9dc6SAnna Dabrowska $id = \spl_object_hash($file); 158*dc4d9dc6SAnna Dabrowska } else { 159*dc4d9dc6SAnna Dabrowska throw new \InvalidArgumentException( 160*dc4d9dc6SAnna Dabrowska \sprintf('Invalid type in $file parameter (%s)', \gettype($file)) 161*dc4d9dc6SAnna Dabrowska ); 162*dc4d9dc6SAnna Dabrowska } 163*dc4d9dc6SAnna Dabrowska 164*dc4d9dc6SAnna Dabrowska /** @noinspection OffsetOperationsInspection */ 165*dc4d9dc6SAnna Dabrowska if (isset($this->readers[$id])) { 166*dc4d9dc6SAnna Dabrowska return $id; 167*dc4d9dc6SAnna Dabrowska } 168*dc4d9dc6SAnna Dabrowska 169*dc4d9dc6SAnna Dabrowska if (\is_resource($file)) { 170*dc4d9dc6SAnna Dabrowska $streamReader = new StreamReader($file); 171*dc4d9dc6SAnna Dabrowska } elseif (\is_string($file)) { 172*dc4d9dc6SAnna Dabrowska $streamReader = StreamReader::createByFile($file); 173*dc4d9dc6SAnna Dabrowska $this->createdReaders[] = $id; 174*dc4d9dc6SAnna Dabrowska } else { 175*dc4d9dc6SAnna Dabrowska $streamReader = $file; 176*dc4d9dc6SAnna Dabrowska } 177*dc4d9dc6SAnna Dabrowska 178*dc4d9dc6SAnna Dabrowska $reader = new PdfReader($this->getPdfParserInstance($streamReader)); 179*dc4d9dc6SAnna Dabrowska /** @noinspection OffsetOperationsInspection */ 180*dc4d9dc6SAnna Dabrowska $this->readers[$id] = $reader; 181*dc4d9dc6SAnna Dabrowska 182*dc4d9dc6SAnna Dabrowska return $id; 183*dc4d9dc6SAnna Dabrowska } 184*dc4d9dc6SAnna Dabrowska 185*dc4d9dc6SAnna Dabrowska /** 186*dc4d9dc6SAnna Dabrowska * Get a pdf reader instance by its id. 187*dc4d9dc6SAnna Dabrowska * 188*dc4d9dc6SAnna Dabrowska * @param string $id 189*dc4d9dc6SAnna Dabrowska * @return PdfReader 190*dc4d9dc6SAnna Dabrowska */ 191*dc4d9dc6SAnna Dabrowska protected function getPdfReader($id) 192*dc4d9dc6SAnna Dabrowska { 193*dc4d9dc6SAnna Dabrowska if (isset($this->readers[$id])) { 194*dc4d9dc6SAnna Dabrowska return $this->readers[$id]; 195*dc4d9dc6SAnna Dabrowska } 196*dc4d9dc6SAnna Dabrowska 197*dc4d9dc6SAnna Dabrowska throw new \InvalidArgumentException( 198*dc4d9dc6SAnna Dabrowska \sprintf('No pdf reader with the given id (%s) exists.', $id) 199*dc4d9dc6SAnna Dabrowska ); 200*dc4d9dc6SAnna Dabrowska } 201*dc4d9dc6SAnna Dabrowska 202*dc4d9dc6SAnna Dabrowska /** 203*dc4d9dc6SAnna Dabrowska * Set the source PDF file. 204*dc4d9dc6SAnna Dabrowska * 205*dc4d9dc6SAnna Dabrowska * @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance. 206*dc4d9dc6SAnna Dabrowska * @return int The page count of the PDF document. 207*dc4d9dc6SAnna Dabrowska * @throws PdfParserException 208*dc4d9dc6SAnna Dabrowska */ 209*dc4d9dc6SAnna Dabrowska public function setSourceFile($file) 210*dc4d9dc6SAnna Dabrowska { 211*dc4d9dc6SAnna Dabrowska $this->currentReaderId = $this->getPdfReaderId($file); 212*dc4d9dc6SAnna Dabrowska $this->objectsToCopy[$this->currentReaderId] = []; 213*dc4d9dc6SAnna Dabrowska 214*dc4d9dc6SAnna Dabrowska $reader = $this->getPdfReader($this->currentReaderId); 215*dc4d9dc6SAnna Dabrowska $this->setMinPdfVersion($reader->getPdfVersion()); 216*dc4d9dc6SAnna Dabrowska 217*dc4d9dc6SAnna Dabrowska return $reader->getPageCount(); 218*dc4d9dc6SAnna Dabrowska } 219*dc4d9dc6SAnna Dabrowska 220*dc4d9dc6SAnna Dabrowska /** 221*dc4d9dc6SAnna Dabrowska * Imports a page. 222*dc4d9dc6SAnna Dabrowska * 223*dc4d9dc6SAnna Dabrowska * @param int $pageNumber The page number. 224*dc4d9dc6SAnna Dabrowska * @param string $box The page boundary to import. Default set to PageBoundaries::CROP_BOX. 225*dc4d9dc6SAnna Dabrowska * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used). 226*dc4d9dc6SAnna Dabrowska * @return string A unique string identifying the imported page. 227*dc4d9dc6SAnna Dabrowska * @throws CrossReferenceException 228*dc4d9dc6SAnna Dabrowska * @throws FilterException 229*dc4d9dc6SAnna Dabrowska * @throws PdfParserException 230*dc4d9dc6SAnna Dabrowska * @throws PdfTypeException 231*dc4d9dc6SAnna Dabrowska * @throws PdfReaderException 232*dc4d9dc6SAnna Dabrowska * @see PageBoundaries 233*dc4d9dc6SAnna Dabrowska */ 234*dc4d9dc6SAnna Dabrowska public function importPage($pageNumber, $box = PageBoundaries::CROP_BOX, $groupXObject = true) 235*dc4d9dc6SAnna Dabrowska { 236*dc4d9dc6SAnna Dabrowska if (null === $this->currentReaderId) { 237*dc4d9dc6SAnna Dabrowska throw new \BadMethodCallException('No reader initiated. Call setSourceFile() first.'); 238*dc4d9dc6SAnna Dabrowska } 239*dc4d9dc6SAnna Dabrowska 240*dc4d9dc6SAnna Dabrowska $pageId = $this->currentReaderId; 241*dc4d9dc6SAnna Dabrowska 242*dc4d9dc6SAnna Dabrowska $pageNumber = (int)$pageNumber; 243*dc4d9dc6SAnna Dabrowska $pageId .= '|' . $pageNumber . '|' . ($groupXObject ? '1' : '0'); 244*dc4d9dc6SAnna Dabrowska 245*dc4d9dc6SAnna Dabrowska // for backwards compatibility with FPDI 1 246*dc4d9dc6SAnna Dabrowska $box = \ltrim($box, '/'); 247*dc4d9dc6SAnna Dabrowska if (!PageBoundaries::isValidName($box)) { 248*dc4d9dc6SAnna Dabrowska throw new \InvalidArgumentException( 249*dc4d9dc6SAnna Dabrowska \sprintf('Box name is invalid: "%s"', $box) 250*dc4d9dc6SAnna Dabrowska ); 251*dc4d9dc6SAnna Dabrowska } 252*dc4d9dc6SAnna Dabrowska 253*dc4d9dc6SAnna Dabrowska $pageId .= '|' . $box; 254*dc4d9dc6SAnna Dabrowska 255*dc4d9dc6SAnna Dabrowska if (isset($this->importedPages[$pageId])) { 256*dc4d9dc6SAnna Dabrowska return $pageId; 257*dc4d9dc6SAnna Dabrowska } 258*dc4d9dc6SAnna Dabrowska 259*dc4d9dc6SAnna Dabrowska $reader = $this->getPdfReader($this->currentReaderId); 260*dc4d9dc6SAnna Dabrowska $page = $reader->getPage($pageNumber); 261*dc4d9dc6SAnna Dabrowska 262*dc4d9dc6SAnna Dabrowska $bbox = $page->getBoundary($box); 263*dc4d9dc6SAnna Dabrowska if ($bbox === false) { 264*dc4d9dc6SAnna Dabrowska throw new PdfReaderException( 265*dc4d9dc6SAnna Dabrowska \sprintf("Page doesn't have a boundary box (%s).", $box), 266*dc4d9dc6SAnna Dabrowska PdfReaderException::MISSING_DATA 267*dc4d9dc6SAnna Dabrowska ); 268*dc4d9dc6SAnna Dabrowska } 269*dc4d9dc6SAnna Dabrowska 270*dc4d9dc6SAnna Dabrowska $dict = new PdfDictionary(); 271*dc4d9dc6SAnna Dabrowska $dict->value['Type'] = PdfName::create('XObject'); 272*dc4d9dc6SAnna Dabrowska $dict->value['Subtype'] = PdfName::create('Form'); 273*dc4d9dc6SAnna Dabrowska $dict->value['FormType'] = PdfNumeric::create(1); 274*dc4d9dc6SAnna Dabrowska $dict->value['BBox'] = $bbox->toPdfArray(); 275*dc4d9dc6SAnna Dabrowska 276*dc4d9dc6SAnna Dabrowska if ($groupXObject) { 277*dc4d9dc6SAnna Dabrowska $this->setMinPdfVersion('1.4'); 278*dc4d9dc6SAnna Dabrowska $dict->value['Group'] = PdfDictionary::create([ 279*dc4d9dc6SAnna Dabrowska 'Type' => PdfName::create('Group'), 280*dc4d9dc6SAnna Dabrowska 'S' => PdfName::create('Transparency') 281*dc4d9dc6SAnna Dabrowska ]); 282*dc4d9dc6SAnna Dabrowska } 283*dc4d9dc6SAnna Dabrowska 284*dc4d9dc6SAnna Dabrowska $resources = $page->getAttribute('Resources'); 285*dc4d9dc6SAnna Dabrowska if ($resources !== null) { 286*dc4d9dc6SAnna Dabrowska $dict->value['Resources'] = $resources; 287*dc4d9dc6SAnna Dabrowska } 288*dc4d9dc6SAnna Dabrowska 289*dc4d9dc6SAnna Dabrowska list($width, $height) = $page->getWidthAndHeight($box); 290*dc4d9dc6SAnna Dabrowska 291*dc4d9dc6SAnna Dabrowska $a = 1; 292*dc4d9dc6SAnna Dabrowska $b = 0; 293*dc4d9dc6SAnna Dabrowska $c = 0; 294*dc4d9dc6SAnna Dabrowska $d = 1; 295*dc4d9dc6SAnna Dabrowska $e = -$bbox->getLlx(); 296*dc4d9dc6SAnna Dabrowska $f = -$bbox->getLly(); 297*dc4d9dc6SAnna Dabrowska 298*dc4d9dc6SAnna Dabrowska $rotation = $page->getRotation(); 299*dc4d9dc6SAnna Dabrowska 300*dc4d9dc6SAnna Dabrowska if ($rotation !== 0) { 301*dc4d9dc6SAnna Dabrowska $rotation *= -1; 302*dc4d9dc6SAnna Dabrowska $angle = $rotation * M_PI/180; 303*dc4d9dc6SAnna Dabrowska $a = \cos($angle); 304*dc4d9dc6SAnna Dabrowska $b = \sin($angle); 305*dc4d9dc6SAnna Dabrowska $c = -$b; 306*dc4d9dc6SAnna Dabrowska $d = $a; 307*dc4d9dc6SAnna Dabrowska 308*dc4d9dc6SAnna Dabrowska switch ($rotation) { 309*dc4d9dc6SAnna Dabrowska case -90: 310*dc4d9dc6SAnna Dabrowska $e = -$bbox->getLly(); 311*dc4d9dc6SAnna Dabrowska $f = $bbox->getUrx(); 312*dc4d9dc6SAnna Dabrowska break; 313*dc4d9dc6SAnna Dabrowska case -180: 314*dc4d9dc6SAnna Dabrowska $e = $bbox->getUrx(); 315*dc4d9dc6SAnna Dabrowska $f = $bbox->getUry(); 316*dc4d9dc6SAnna Dabrowska break; 317*dc4d9dc6SAnna Dabrowska case -270: 318*dc4d9dc6SAnna Dabrowska $e = $bbox->getUry(); 319*dc4d9dc6SAnna Dabrowska $f = -$bbox->getLlx(); 320*dc4d9dc6SAnna Dabrowska break; 321*dc4d9dc6SAnna Dabrowska } 322*dc4d9dc6SAnna Dabrowska } 323*dc4d9dc6SAnna Dabrowska 324*dc4d9dc6SAnna Dabrowska // we need to rotate/translate 325*dc4d9dc6SAnna Dabrowska if ($a != 1 || $b != 0 || $c != 0 || $d != 1 || $e != 0 || $f != 0) { 326*dc4d9dc6SAnna Dabrowska $dict->value['Matrix'] = PdfArray::create([ 327*dc4d9dc6SAnna Dabrowska PdfNumeric::create($a), PdfNumeric::create($b), PdfNumeric::create($c), 328*dc4d9dc6SAnna Dabrowska PdfNumeric::create($d), PdfNumeric::create($e), PdfNumeric::create($f) 329*dc4d9dc6SAnna Dabrowska ]); 330*dc4d9dc6SAnna Dabrowska } 331*dc4d9dc6SAnna Dabrowska 332*dc4d9dc6SAnna Dabrowska // try to use the existing content stream 333*dc4d9dc6SAnna Dabrowska $pageDict = $page->getPageDictionary(); 334*dc4d9dc6SAnna Dabrowska 335*dc4d9dc6SAnna Dabrowska $contentsObject = PdfType::resolve(PdfDictionary::get($pageDict, 'Contents'), $reader->getParser(), true); 336*dc4d9dc6SAnna Dabrowska $contents = PdfType::resolve($contentsObject, $reader->getParser()); 337*dc4d9dc6SAnna Dabrowska 338*dc4d9dc6SAnna Dabrowska // just copy the stream reference if it is only a single stream 339*dc4d9dc6SAnna Dabrowska if (($contentsIsStream = ($contents instanceof PdfStream)) 340*dc4d9dc6SAnna Dabrowska || ($contents instanceof PdfArray && \count($contents->value) === 1) 341*dc4d9dc6SAnna Dabrowska ) { 342*dc4d9dc6SAnna Dabrowska if ($contentsIsStream) { 343*dc4d9dc6SAnna Dabrowska /** 344*dc4d9dc6SAnna Dabrowska * @var PdfIndirectObject $contentsObject 345*dc4d9dc6SAnna Dabrowska */ 346*dc4d9dc6SAnna Dabrowska $stream = $contents; 347*dc4d9dc6SAnna Dabrowska } else { 348*dc4d9dc6SAnna Dabrowska $stream = PdfType::resolve($contents->value[0], $reader->getParser()); 349*dc4d9dc6SAnna Dabrowska } 350*dc4d9dc6SAnna Dabrowska 351*dc4d9dc6SAnna Dabrowska $filter = PdfDictionary::get($stream->value, 'Filter'); 352*dc4d9dc6SAnna Dabrowska if (!$filter instanceof PdfNull) { 353*dc4d9dc6SAnna Dabrowska $dict->value['Filter'] = $filter; 354*dc4d9dc6SAnna Dabrowska } 355*dc4d9dc6SAnna Dabrowska $length = PdfType::resolve(PdfDictionary::get($stream->value, 'Length'), $reader->getParser()); 356*dc4d9dc6SAnna Dabrowska $dict->value['Length'] = $length; 357*dc4d9dc6SAnna Dabrowska $stream->value = $dict; 358*dc4d9dc6SAnna Dabrowska 359*dc4d9dc6SAnna Dabrowska // otherwise extract it from the array and re-compress the whole stream 360*dc4d9dc6SAnna Dabrowska } else { 361*dc4d9dc6SAnna Dabrowska $streamContent = $this->compress 362*dc4d9dc6SAnna Dabrowska ? \gzcompress($page->getContentStream()) 363*dc4d9dc6SAnna Dabrowska : $page->getContentStream(); 364*dc4d9dc6SAnna Dabrowska 365*dc4d9dc6SAnna Dabrowska $dict->value['Length'] = PdfNumeric::create(\strlen($streamContent)); 366*dc4d9dc6SAnna Dabrowska if ($this->compress) { 367*dc4d9dc6SAnna Dabrowska $dict->value['Filter'] = PdfName::create('FlateDecode'); 368*dc4d9dc6SAnna Dabrowska } 369*dc4d9dc6SAnna Dabrowska 370*dc4d9dc6SAnna Dabrowska $stream = PdfStream::create($dict, $streamContent); 371*dc4d9dc6SAnna Dabrowska } 372*dc4d9dc6SAnna Dabrowska 373*dc4d9dc6SAnna Dabrowska $this->importedPages[$pageId] = [ 374*dc4d9dc6SAnna Dabrowska 'objectNumber' => null, 375*dc4d9dc6SAnna Dabrowska 'readerId' => $this->currentReaderId, 376*dc4d9dc6SAnna Dabrowska 'id' => 'TPL' . $this->getNextTemplateId(), 377*dc4d9dc6SAnna Dabrowska 'width' => $width / $this->k, 378*dc4d9dc6SAnna Dabrowska 'height' => $height / $this->k, 379*dc4d9dc6SAnna Dabrowska 'stream' => $stream 380*dc4d9dc6SAnna Dabrowska ]; 381*dc4d9dc6SAnna Dabrowska 382*dc4d9dc6SAnna Dabrowska return $pageId; 383*dc4d9dc6SAnna Dabrowska } 384*dc4d9dc6SAnna Dabrowska 385*dc4d9dc6SAnna Dabrowska /** 386*dc4d9dc6SAnna Dabrowska * Draws an imported page onto the page. 387*dc4d9dc6SAnna Dabrowska * 388*dc4d9dc6SAnna Dabrowska * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the 389*dc4d9dc6SAnna Dabrowska * aspect ratio. 390*dc4d9dc6SAnna Dabrowska * 391*dc4d9dc6SAnna Dabrowska * @param mixed $pageId The page id 392*dc4d9dc6SAnna Dabrowska * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array 393*dc4d9dc6SAnna Dabrowska * with the keys "x", "y", "width", "height", "adjustPageSize". 394*dc4d9dc6SAnna Dabrowska * @param float|int $y The ordinate of upper-left corner. 395*dc4d9dc6SAnna Dabrowska * @param float|int|null $width The width. 396*dc4d9dc6SAnna Dabrowska * @param float|int|null $height The height. 397*dc4d9dc6SAnna Dabrowska * @param bool $adjustPageSize 398*dc4d9dc6SAnna Dabrowska * @return array The size. 399*dc4d9dc6SAnna Dabrowska * @see Fpdi::getTemplateSize() 400*dc4d9dc6SAnna Dabrowska */ 401*dc4d9dc6SAnna Dabrowska public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) 402*dc4d9dc6SAnna Dabrowska { 403*dc4d9dc6SAnna Dabrowska if (\is_array($x)) { 404*dc4d9dc6SAnna Dabrowska /** @noinspection OffsetOperationsInspection */ 405*dc4d9dc6SAnna Dabrowska unset($x['pageId']); 406*dc4d9dc6SAnna Dabrowska \extract($x, EXTR_IF_EXISTS); 407*dc4d9dc6SAnna Dabrowska /** @noinspection NotOptimalIfConditionsInspection */ 408*dc4d9dc6SAnna Dabrowska if (\is_array($x)) { 409*dc4d9dc6SAnna Dabrowska $x = 0; 410*dc4d9dc6SAnna Dabrowska } 411*dc4d9dc6SAnna Dabrowska } 412*dc4d9dc6SAnna Dabrowska 413*dc4d9dc6SAnna Dabrowska if (!isset($this->importedPages[$pageId])) { 414*dc4d9dc6SAnna Dabrowska throw new \InvalidArgumentException('Imported page does not exist!'); 415*dc4d9dc6SAnna Dabrowska } 416*dc4d9dc6SAnna Dabrowska 417*dc4d9dc6SAnna Dabrowska $importedPage = $this->importedPages[$pageId]; 418*dc4d9dc6SAnna Dabrowska 419*dc4d9dc6SAnna Dabrowska $originalSize = $this->getTemplateSize($pageId); 420*dc4d9dc6SAnna Dabrowska $newSize = $this->getTemplateSize($pageId, $width, $height); 421*dc4d9dc6SAnna Dabrowska if ($adjustPageSize) { 422*dc4d9dc6SAnna Dabrowska $this->setPageFormat($newSize, $newSize['orientation']); 423*dc4d9dc6SAnna Dabrowska } 424*dc4d9dc6SAnna Dabrowska 425*dc4d9dc6SAnna Dabrowska $this->_out( 426*dc4d9dc6SAnna Dabrowska // reset standard values, translate and scale 427*dc4d9dc6SAnna Dabrowska \sprintf( 428*dc4d9dc6SAnna Dabrowska 'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q', 429*dc4d9dc6SAnna Dabrowska ($newSize['width'] / $originalSize['width']), 430*dc4d9dc6SAnna Dabrowska ($newSize['height'] / $originalSize['height']), 431*dc4d9dc6SAnna Dabrowska $x * $this->k, 432*dc4d9dc6SAnna Dabrowska ($this->h - $y - $newSize['height']) * $this->k, 433*dc4d9dc6SAnna Dabrowska $importedPage['id'] 434*dc4d9dc6SAnna Dabrowska ) 435*dc4d9dc6SAnna Dabrowska ); 436*dc4d9dc6SAnna Dabrowska 437*dc4d9dc6SAnna Dabrowska return $newSize; 438*dc4d9dc6SAnna Dabrowska } 439*dc4d9dc6SAnna Dabrowska 440*dc4d9dc6SAnna Dabrowska /** 441*dc4d9dc6SAnna Dabrowska * Get the size of an imported page. 442*dc4d9dc6SAnna Dabrowska * 443*dc4d9dc6SAnna Dabrowska * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the 444*dc4d9dc6SAnna Dabrowska * aspect ratio. 445*dc4d9dc6SAnna Dabrowska * 446*dc4d9dc6SAnna Dabrowska * @param mixed $tpl The template id 447*dc4d9dc6SAnna Dabrowska * @param float|int|null $width The width. 448*dc4d9dc6SAnna Dabrowska * @param float|int|null $height The height. 449*dc4d9dc6SAnna Dabrowska * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) 450*dc4d9dc6SAnna Dabrowska */ 451*dc4d9dc6SAnna Dabrowska public function getImportedPageSize($tpl, $width = null, $height = null) 452*dc4d9dc6SAnna Dabrowska { 453*dc4d9dc6SAnna Dabrowska if (isset($this->importedPages[$tpl])) { 454*dc4d9dc6SAnna Dabrowska $importedPage = $this->importedPages[$tpl]; 455*dc4d9dc6SAnna Dabrowska 456*dc4d9dc6SAnna Dabrowska if ($width === null && $height === null) { 457*dc4d9dc6SAnna Dabrowska $width = $importedPage['width']; 458*dc4d9dc6SAnna Dabrowska $height = $importedPage['height']; 459*dc4d9dc6SAnna Dabrowska } elseif ($width === null) { 460*dc4d9dc6SAnna Dabrowska $width = $height * $importedPage['width'] / $importedPage['height']; 461*dc4d9dc6SAnna Dabrowska } 462*dc4d9dc6SAnna Dabrowska 463*dc4d9dc6SAnna Dabrowska if ($height === null) { 464*dc4d9dc6SAnna Dabrowska $height = $width * $importedPage['height'] / $importedPage['width']; 465*dc4d9dc6SAnna Dabrowska } 466*dc4d9dc6SAnna Dabrowska 467*dc4d9dc6SAnna Dabrowska if ($height <= 0. || $width <= 0.) { 468*dc4d9dc6SAnna Dabrowska throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.'); 469*dc4d9dc6SAnna Dabrowska } 470*dc4d9dc6SAnna Dabrowska 471*dc4d9dc6SAnna Dabrowska return [ 472*dc4d9dc6SAnna Dabrowska 'width' => $width, 473*dc4d9dc6SAnna Dabrowska 'height' => $height, 474*dc4d9dc6SAnna Dabrowska 0 => $width, 475*dc4d9dc6SAnna Dabrowska 1 => $height, 476*dc4d9dc6SAnna Dabrowska 'orientation' => $width > $height ? 'L' : 'P' 477*dc4d9dc6SAnna Dabrowska ]; 478*dc4d9dc6SAnna Dabrowska } 479*dc4d9dc6SAnna Dabrowska 480*dc4d9dc6SAnna Dabrowska return false; 481*dc4d9dc6SAnna Dabrowska } 482*dc4d9dc6SAnna Dabrowska 483*dc4d9dc6SAnna Dabrowska /** 484*dc4d9dc6SAnna Dabrowska * Writes a PdfType object to the resulting buffer. 485*dc4d9dc6SAnna Dabrowska * 486*dc4d9dc6SAnna Dabrowska * @param PdfType $value 487*dc4d9dc6SAnna Dabrowska * @throws PdfTypeException 488*dc4d9dc6SAnna Dabrowska */ 489*dc4d9dc6SAnna Dabrowska protected function writePdfType(PdfType $value) 490*dc4d9dc6SAnna Dabrowska { 491*dc4d9dc6SAnna Dabrowska if ($value instanceof PdfNumeric) { 492*dc4d9dc6SAnna Dabrowska if (\is_int($value->value)) { 493*dc4d9dc6SAnna Dabrowska $this->_put($value->value . ' ', false); 494*dc4d9dc6SAnna Dabrowska } else { 495*dc4d9dc6SAnna Dabrowska $this->_put(\rtrim(\rtrim(\sprintf('%.5F', $value->value), '0'), '.') . ' ', false); 496*dc4d9dc6SAnna Dabrowska } 497*dc4d9dc6SAnna Dabrowska 498*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfName) { 499*dc4d9dc6SAnna Dabrowska $this->_put('/' . $value->value . ' ', false); 500*dc4d9dc6SAnna Dabrowska 501*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfString) { 502*dc4d9dc6SAnna Dabrowska $this->_put('(' . $value->value . ')', false); 503*dc4d9dc6SAnna Dabrowska 504*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfHexString) { 505*dc4d9dc6SAnna Dabrowska $this->_put('<' . $value->value . '>'); 506*dc4d9dc6SAnna Dabrowska 507*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfBoolean) { 508*dc4d9dc6SAnna Dabrowska $this->_put($value->value ? 'true ' : 'false ', false); 509*dc4d9dc6SAnna Dabrowska 510*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfArray) { 511*dc4d9dc6SAnna Dabrowska $this->_put('[', false); 512*dc4d9dc6SAnna Dabrowska foreach ($value->value as $entry) { 513*dc4d9dc6SAnna Dabrowska $this->writePdfType($entry); 514*dc4d9dc6SAnna Dabrowska } 515*dc4d9dc6SAnna Dabrowska $this->_put(']'); 516*dc4d9dc6SAnna Dabrowska 517*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfDictionary) { 518*dc4d9dc6SAnna Dabrowska $this->_put('<<', false); 519*dc4d9dc6SAnna Dabrowska foreach ($value->value as $name => $entry) { 520*dc4d9dc6SAnna Dabrowska $this->_put('/' . $name . ' ', false); 521*dc4d9dc6SAnna Dabrowska $this->writePdfType($entry); 522*dc4d9dc6SAnna Dabrowska } 523*dc4d9dc6SAnna Dabrowska $this->_put('>>'); 524*dc4d9dc6SAnna Dabrowska 525*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfToken) { 526*dc4d9dc6SAnna Dabrowska $this->_put($value->value); 527*dc4d9dc6SAnna Dabrowska 528*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfNull) { 529*dc4d9dc6SAnna Dabrowska $this->_put('null '); 530*dc4d9dc6SAnna Dabrowska 531*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfStream) { 532*dc4d9dc6SAnna Dabrowska /** 533*dc4d9dc6SAnna Dabrowska * @var $value PdfStream 534*dc4d9dc6SAnna Dabrowska */ 535*dc4d9dc6SAnna Dabrowska $this->writePdfType($value->value); 536*dc4d9dc6SAnna Dabrowska $this->_put('stream'); 537*dc4d9dc6SAnna Dabrowska $this->_put($value->getStream()); 538*dc4d9dc6SAnna Dabrowska $this->_put('endstream'); 539*dc4d9dc6SAnna Dabrowska 540*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfIndirectObjectReference) { 541*dc4d9dc6SAnna Dabrowska if (!isset($this->objectMap[$this->currentReaderId])) { 542*dc4d9dc6SAnna Dabrowska $this->objectMap[$this->currentReaderId] = []; 543*dc4d9dc6SAnna Dabrowska } 544*dc4d9dc6SAnna Dabrowska 545*dc4d9dc6SAnna Dabrowska if (!isset($this->objectMap[$this->currentReaderId][$value->value])) { 546*dc4d9dc6SAnna Dabrowska $this->objectMap[$this->currentReaderId][$value->value] = ++$this->n; 547*dc4d9dc6SAnna Dabrowska $this->objectsToCopy[$this->currentReaderId][] = $value->value; 548*dc4d9dc6SAnna Dabrowska } 549*dc4d9dc6SAnna Dabrowska 550*dc4d9dc6SAnna Dabrowska $this->_put($this->objectMap[$this->currentReaderId][$value->value] . ' 0 R ', false); 551*dc4d9dc6SAnna Dabrowska 552*dc4d9dc6SAnna Dabrowska } elseif ($value instanceof PdfIndirectObject) { 553*dc4d9dc6SAnna Dabrowska /** 554*dc4d9dc6SAnna Dabrowska * @var $value PdfIndirectObject 555*dc4d9dc6SAnna Dabrowska */ 556*dc4d9dc6SAnna Dabrowska $n = $this->objectMap[$this->currentReaderId][$value->objectNumber]; 557*dc4d9dc6SAnna Dabrowska $this->_newobj($n); 558*dc4d9dc6SAnna Dabrowska $this->writePdfType($value->value); 559*dc4d9dc6SAnna Dabrowska $this->_put('endobj'); 560*dc4d9dc6SAnna Dabrowska } 561*dc4d9dc6SAnna Dabrowska } 562*dc4d9dc6SAnna Dabrowska} 563