FpdiTrait.php 23 KB

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