This commit is contained in:
Sara Arjona 2024-03-11 17:35:53 +01:00
commit 80fe231e74
No known key found for this signature in database
54 changed files with 1119 additions and 439 deletions

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -13,7 +13,7 @@ namespace setasign\Fpdi;
/** /**
* Trait FpdfTplTrait * Trait FpdfTplTrait
* *
* This class adds a templating feature to tFPDF. * This trait adds a templating feature to FPDF and tFPDF.
*/ */
trait FpdfTplTrait trait FpdfTplTrait
{ {
@ -234,7 +234,9 @@ trait FpdfTplTrait
'lMargin' => $this->lMargin, 'lMargin' => $this->lMargin,
'rMargin' => $this->rMargin, 'rMargin' => $this->rMargin,
'h' => $this->h, 'h' => $this->h,
'hPt' => $this->hPt,
'w' => $this->w, 'w' => $this->w,
'wPt' => $this->wPt,
'FontFamily' => $this->FontFamily, 'FontFamily' => $this->FontFamily,
'FontStyle' => $this->FontStyle, 'FontStyle' => $this->FontStyle,
'FontSizePt' => $this->FontSizePt, 'FontSizePt' => $this->FontSizePt,
@ -251,7 +253,9 @@ trait FpdfTplTrait
$this->currentTemplateId = $templateId; $this->currentTemplateId = $templateId;
$this->h = $height; $this->h = $height;
$this->hPt = $height / $this->k;
$this->w = $width; $this->w = $width;
$this->wPt = $width / $this->k;
$this->SetXY($this->lMargin, $this->tMargin); $this->SetXY($this->lMargin, $this->tMargin);
$this->SetRightMargin($this->w - $width + $this->rMargin); $this->SetRightMargin($this->w - $width + $this->rMargin);
@ -279,7 +283,9 @@ trait FpdfTplTrait
$this->lMargin = $state['lMargin']; $this->lMargin = $state['lMargin'];
$this->rMargin = $state['rMargin']; $this->rMargin = $state['rMargin'];
$this->h = $state['h']; $this->h = $state['h'];
$this->hPt = $state['hPt'];
$this->w = $state['w']; $this->w = $state['w'];
$this->wPt = $state['wPt'];
$this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']); $this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']);
$this->FontFamily = $state['FontFamily']; $this->FontFamily = $state['FontFamily'];
@ -406,9 +412,6 @@ trait FpdfTplTrait
} }
} }
/**
* @inheritdoc
*/
protected function _putimages() protected function _putimages()
{ {
parent::_putimages(); parent::_putimages();

View file

@ -0,0 +1,192 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfType;
/**
* This trait is used for the implementation of FPDI in FPDF and tFPDF.
*/
trait FpdfTrait
{
protected function _enddoc()
{
parent::_enddoc();
$this->cleanUp();
}
/**
* Draws an imported page or a template onto the page or another template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size
* @see Fpdi::getTemplateSize()
*/
public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (isset($this->importedPages[$tpl])) {
$size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
if ($this->currentTemplateId !== null) {
$this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl;
}
return $size;
}
return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize);
}
/**
* Get the size of an imported page or template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getTemplateSize($tpl, $width = null, $height = null)
{
$size = parent::getTemplateSize($tpl, $width, $height);
if ($size === false) {
return $this->getImportedPageSize($tpl, $width, $height);
}
return $size;
}
/**
* @throws CrossReferenceException
* @throws PdfParserException
*/
protected function _putimages()
{
$this->currentReaderId = null;
parent::_putimages();
foreach ($this->importedPages as $key => $pageData) {
$this->_newobj();
$this->importedPages[$key]['objectNumber'] = $this->n;
$this->currentReaderId = $pageData['readerId'];
$this->writePdfType($pageData['stream']);
$this->_put('endobj');
}
foreach (\array_keys($this->readers) as $readerId) {
$parser = $this->getPdfReader($readerId)->getParser();
$this->currentReaderId = $readerId;
while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
try {
$object = $parser->getIndirectObject($objectNumber);
} catch (CrossReferenceException $e) {
if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
$object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
} else {
throw $e;
}
}
$this->writePdfType($object);
}
}
$this->currentReaderId = null;
}
/**
* @inheritdoc
*/
protected function _putxobjectdict()
{
foreach ($this->importedPages as $pageData) {
$this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R');
}
parent::_putxobjectdict();
}
/**
* @param int $n
* @return void
* @throws PdfParser\Type\PdfTypeException
*/
protected function _putlinks($n)
{
foreach ($this->PageLinks[$n] as $pl) {
$this->_newobj();
$rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
$this->_put('<</Type /Annot /Subtype /Link /Rect [' . $rect . ']', false);
if (is_string($pl[4])) {
$this->_put('/A <</S /URI /URI ' . $this->_textstring($pl[4]) . '>>');
if (isset($pl['importedLink'])) {
$values = $pl['importedLink']['pdfObject']->value;
foreach ($values as $name => $entry) {
$this->_put('/' . $name . ' ', false);
$this->writePdfType($entry);
}
if (isset($pl['quadPoints'])) {
$s = '/QuadPoints[';
foreach ($pl['quadPoints'] as $value) {
$s .= sprintf('%.2F ', $value);
}
$s .= ']';
$this->_put($s);
}
} else {
$this->_put('/Border [0 0 0]', false);
}
$this->_put('>>');
} else {
$this->_put('/Border [0 0 0] ', false);
$l = $this->links[$pl[4]];
if (isset($this->PageInfo[$l[0]]['size'])) {
$h = $this->PageInfo[$l[0]]['size'][1];
} else {
$h = ($this->DefOrientation === 'P')
? $this->DefPageSize[1] * $this->k
: $this->DefPageSize[0] * $this->k;
}
$this->_put(sprintf(
'/Dest [%d 0 R /XYZ 0 %.2F null]>>',
$this->PageInfo[$l[0]]['n'],
$h - $l[1] * $this->k
));
}
$this->_put('endobj');
}
}
protected function _put($s, $newLine = true)
{
if ($newLine) {
$this->buffer .= $s . "\n";
} else {
$this->buffer .= $s;
}
}
}

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -23,131 +23,12 @@ use setasign\Fpdi\PdfParser\Type\PdfNull;
class Fpdi extends FpdfTpl class Fpdi extends FpdfTpl
{ {
use FpdiTrait; use FpdiTrait;
use FpdfTrait;
/** /**
* FPDI version * FPDI version
* *
* @string * @string
*/ */
const VERSION = '2.3.7'; const VERSION = '2.6.0';
protected function _enddoc()
{
parent::_enddoc();
$this->cleanUp();
}
/**
* Draws an imported page or a template onto the page or another template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size
* @see Fpdi::getTemplateSize()
*/
public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (isset($this->importedPages[$tpl])) {
$size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
if ($this->currentTemplateId !== null) {
$this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl;
}
return $size;
}
return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize);
}
/**
* Get the size of an imported page or template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getTemplateSize($tpl, $width = null, $height = null)
{
$size = parent::getTemplateSize($tpl, $width, $height);
if ($size === false) {
return $this->getImportedPageSize($tpl, $width, $height);
}
return $size;
}
/**
* @inheritdoc
* @throws CrossReferenceException
* @throws PdfParserException
*/
protected function _putimages()
{
$this->currentReaderId = null;
parent::_putimages();
foreach ($this->importedPages as $key => $pageData) {
$this->_newobj();
$this->importedPages[$key]['objectNumber'] = $this->n;
$this->currentReaderId = $pageData['readerId'];
$this->writePdfType($pageData['stream']);
$this->_put('endobj');
}
foreach (\array_keys($this->readers) as $readerId) {
$parser = $this->getPdfReader($readerId)->getParser();
$this->currentReaderId = $readerId;
while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
try {
$object = $parser->getIndirectObject($objectNumber);
} catch (CrossReferenceException $e) {
if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
$object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
} else {
throw $e;
}
}
$this->writePdfType($object);
}
}
$this->currentReaderId = null;
}
/**
* @inheritdoc
*/
protected function _putxobjectdict()
{
foreach ($this->importedPages as $key => $pageData) {
$this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R');
}
parent::_putxobjectdict();
}
/**
* @inheritdoc
*/
protected function _put($s, $newLine = true)
{
if ($newLine) {
$this->buffer .= $s . "\n";
} else {
$this->buffer .= $s;
}
}
} }

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -29,6 +29,7 @@ use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfToken; use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfType; use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException; use setasign\Fpdi\PdfParser\Type\PdfTypeException;
use setasign\Fpdi\PdfReader\DataStructure\Rectangle;
use setasign\Fpdi\PdfReader\PageBoundaries; use setasign\Fpdi\PdfReader\PageBoundaries;
use setasign\Fpdi\PdfReader\PdfReader; use setasign\Fpdi\PdfReader\PdfReader;
use setasign\Fpdi\PdfReader\PdfReaderException; use setasign\Fpdi\PdfReader\PdfReaderException;
@ -123,17 +124,18 @@ trait FpdiTrait
* Get a new pdf parser instance. * Get a new pdf parser instance.
* *
* @param StreamReader $streamReader * @param StreamReader $streamReader
* @param array $parserParams Individual parameters passed to the parser instance.
* @return PdfParser|FpdiPdfParser * @return PdfParser|FpdiPdfParser
*/ */
protected function getPdfParserInstance(StreamReader $streamReader) protected function getPdfParserInstance(StreamReader $streamReader, array $parserParams = [])
{ {
// note: if you get an exception here - turn off errors/warnings on not found for your autoloader. // note: if you get an exception here - turn off errors/warnings on not found classes for your autoloader.
// psr-4 (https://www.php-fig.org/psr/psr-4/) says: Autoloader implementations MUST NOT throw // psr-4 (https://www.php-fig.org/psr/psr-4/) says: Autoloader implementations MUST NOT throw
// exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value. // exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value.
/** @noinspection PhpUndefinedClassInspection */ /** @noinspection PhpUndefinedClassInspection */
if (\class_exists(FpdiPdfParser::class)) { if (\class_exists(FpdiPdfParser::class)) {
/** @noinspection PhpUndefinedClassInspection */ /** @noinspection PhpUndefinedClassInspection */
return new FpdiPdfParser($streamReader); return new FpdiPdfParser($streamReader, $parserParams);
} }
return new PdfParser($streamReader); return new PdfParser($streamReader);
@ -144,9 +146,10 @@ trait FpdiTrait
* *
* @param string|resource|PdfReader|StreamReader $file An open file descriptor, a path to a file, a PdfReader * @param string|resource|PdfReader|StreamReader $file An open file descriptor, a path to a file, a PdfReader
* instance or a StreamReader instance. * instance or a StreamReader instance.
* @param array $parserParams Individual parameters passed to the parser instance.
* @return string * @return string
*/ */
protected function getPdfReaderId($file) protected function getPdfReaderId($file, array $parserParams = [])
{ {
if (\is_resource($file)) { if (\is_resource($file)) {
$id = (string) $file; $id = (string) $file;
@ -177,7 +180,7 @@ trait FpdiTrait
$streamReader = $file; $streamReader = $file;
} }
$reader = new PdfReader($this->getPdfParserInstance($streamReader)); $reader = new PdfReader($this->getPdfParserInstance($streamReader, $parserParams));
/** @noinspection OffsetOperationsInspection */ /** @noinspection OffsetOperationsInspection */
$this->readers[$id] = $reader; $this->readers[$id] = $reader;
@ -210,7 +213,24 @@ trait FpdiTrait
*/ */
public function setSourceFile($file) public function setSourceFile($file)
{ {
$this->currentReaderId = $this->getPdfReaderId($file); return $this->setSourceFileWithParserParams($file);
}
/**
* Set the source PDF file with parameters which are passed to the parser instance.
*
* This method allows us to pass e.g. authentication information to the parser instance.
*
* @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance.
* @param array $parserParams Individual parameters passed to the parser instance.
* @return int The page count of the PDF document.
* @throws CrossReferenceException
* @throws PdfParserException
* @throws PdfTypeException
*/
public function setSourceFileWithParserParams($file, array $parserParams = [])
{
$this->currentReaderId = $this->getPdfReaderId($file, $parserParams);
$this->objectsToCopy[$this->currentReaderId] = []; $this->objectsToCopy[$this->currentReaderId] = [];
$reader = $this->getPdfReader($this->currentReaderId); $reader = $this->getPdfReader($this->currentReaderId);
@ -225,6 +245,7 @@ trait FpdiTrait
* @param int $pageNumber The page number. * @param int $pageNumber The page number.
* @param string $box The page boundary to import. Default set to PageBoundaries::CROP_BOX. * @param string $box The page boundary to import. Default set to PageBoundaries::CROP_BOX.
* @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used). * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used).
* @param bool $importExternalLinks Define whether external links are imported or not.
* @return string A unique string identifying the imported page. * @return string A unique string identifying the imported page.
* @throws CrossReferenceException * @throws CrossReferenceException
* @throws FilterException * @throws FilterException
@ -233,16 +254,20 @@ trait FpdiTrait
* @throws PdfReaderException * @throws PdfReaderException
* @see PageBoundaries * @see PageBoundaries
*/ */
public function importPage($pageNumber, $box = PageBoundaries::CROP_BOX, $groupXObject = true) public function importPage(
{ $pageNumber,
if (null === $this->currentReaderId) { $box = PageBoundaries::CROP_BOX,
$groupXObject = true,
$importExternalLinks = false
) {
if ($this->currentReaderId === null) {
throw new \BadMethodCallException('No reader initiated. Call setSourceFile() first.'); throw new \BadMethodCallException('No reader initiated. Call setSourceFile() first.');
} }
$pageId = $this->currentReaderId; $pageId = $this->currentReaderId;
$pageNumber = (int)$pageNumber; $pageNumber = (int)$pageNumber;
$pageId .= '|' . $pageNumber . '|' . ($groupXObject ? '1' : '0'); $pageId .= '|' . $pageNumber . '|' . ($groupXObject ? '1' : '0') . '|' . ($importExternalLinks ? '1' : '0');
// for backwards compatibility with FPDI 1 // for backwards compatibility with FPDI 1
$box = \ltrim($box, '/'); $box = \ltrim($box, '/');
@ -378,13 +403,19 @@ trait FpdiTrait
$stream = PdfStream::create($dict, ''); $stream = PdfStream::create($dict, '');
} }
$externalLinks = [];
if ($importExternalLinks) {
$externalLinks = $page->getExternalLinks($box);
}
$this->importedPages[$pageId] = [ $this->importedPages[$pageId] = [
'objectNumber' => null, 'objectNumber' => null,
'readerId' => $this->currentReaderId, 'readerId' => $this->currentReaderId,
'id' => 'TPL' . $this->getNextTemplateId(), 'id' => 'TPL' . $this->getNextTemplateId(),
'width' => $width / $this->k, 'width' => $width / $this->k,
'height' => $height / $this->k, 'height' => $height / $this->k,
'stream' => $stream 'stream' => $stream,
'externalLinks' => $externalLinks
]; ];
return $pageId; return $pageId;
@ -430,21 +461,79 @@ trait FpdiTrait
$this->setPageFormat($newSize, $newSize['orientation']); $this->setPageFormat($newSize, $newSize['orientation']);
} }
$scaleX = ($newSize['width'] / $originalSize['width']);
$scaleY = ($newSize['height'] / $originalSize['height']);
$xPt = $x * $this->k;
$yPt = $y * $this->k;
$newHeightPt = $newSize['height'] * $this->k;
$this->_out( $this->_out(
// reset standard values, translate and scale // reset standard values, translate and scale
\sprintf( \sprintf(
'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q', 'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q',
($newSize['width'] / $originalSize['width']), $scaleX,
($newSize['height'] / $originalSize['height']), $scaleY,
$x * $this->k, $xPt,
($this->h - $y - $newSize['height']) * $this->k, $this->hPt - $yPt - $newHeightPt,
$importedPage['id'] $importedPage['id']
) )
); );
if (count($importedPage['externalLinks']) > 0) {
foreach ($importedPage['externalLinks'] as $externalLink) {
// mPDF uses also 'externalLinks' but doesn't come with a rect-value
if (!isset($externalLink['rect'])) {
continue;
}
/** @var Rectangle $rect */
$rect = $externalLink['rect'];
$this->Link(
$x + $rect->getLlx() / $this->k * $scaleX,
$y + $newSize['height'] - ($rect->getLly() + $rect->getHeight()) / $this->k * $scaleY,
$rect->getWidth() / $this->k * $scaleX,
$rect->getHeight() / $this->k * $scaleY,
$externalLink['uri']
);
$this->adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage);
}
}
return $newSize; return $newSize;
} }
/**
* This method will add additional data to the last created link/annotation.
*
* It is separated because TCPDF uses its own logic to handle link annotations.
* This method is overwritten in the TCPDF implementation.
*
* @param array $externalLink
* @param float|int $xPt
* @param float|int $scaleX
* @param float|int $yPt
* @param float|int $newHeightPt
* @param float|int $scaleY
* @param array $importedPage
* @return void
*/
protected function adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage)
{
// let's create a relation of the newly created link to the data of the external link
$lastLink = count($this->PageLinks[$this->page]);
$this->PageLinks[$this->page][$lastLink - 1]['importedLink'] = $externalLink;
if (count($externalLink['quadPoints']) > 0) {
$quadPoints = [];
for ($i = 0, $n = count($externalLink['quadPoints']); $i < $n; $i += 2) {
$quadPoints[] = $xPt + $externalLink['quadPoints'][$i] * $scaleX;
$quadPoints[] = $this->hPt - $yPt - $newHeightPt + $externalLink['quadPoints'][$i + 1] * $scaleY;
}
$this->PageLinks[$this->page][$lastLink - 1]['quadPoints'] = $quadPoints;
}
}
/** /**
* Get the size of an imported page. * Get the size of an imported page.
* *
@ -507,7 +596,7 @@ trait FpdiTrait
} elseif ($value instanceof PdfString) { } elseif ($value instanceof PdfString) {
$this->_put('(' . $value->value . ')', false); $this->_put('(' . $value->value . ')', false);
} elseif ($value instanceof PdfHexString) { } elseif ($value instanceof PdfHexString) {
$this->_put('<' . $value->value . '>'); $this->_put('<' . $value->value . '>', false);
} elseif ($value instanceof PdfBoolean) { } elseif ($value instanceof PdfBoolean) {
$this->_put($value->value ? 'true ' : 'false ', false); $this->_put($value->value ? 'true ' : 'false ', false);
} elseif ($value instanceof PdfArray) { } elseif ($value instanceof PdfArray) {
@ -526,11 +615,8 @@ trait FpdiTrait
} elseif ($value instanceof PdfToken) { } elseif ($value instanceof PdfToken) {
$this->_put($value->value); $this->_put($value->value);
} elseif ($value instanceof PdfNull) { } elseif ($value instanceof PdfNull) {
$this->_put('null '); $this->_put('null ', false);
} elseif ($value instanceof PdfStream) { } elseif ($value instanceof PdfStream) {
/**
* @var $value PdfStream
*/
$this->writePdfType($value->value); $this->writePdfType($value->value);
$this->_put('stream'); $this->_put('stream');
$this->_put($value->getStream()); $this->_put($value->getStream());
@ -547,12 +633,22 @@ trait FpdiTrait
$this->_put($this->objectMap[$this->currentReaderId][$value->value] . ' 0 R ', false); $this->_put($this->objectMap[$this->currentReaderId][$value->value] . ' 0 R ', false);
} elseif ($value instanceof PdfIndirectObject) { } elseif ($value instanceof PdfIndirectObject) {
/**
* @var PdfIndirectObject $value
*/
$n = $this->objectMap[$this->currentReaderId][$value->objectNumber]; $n = $this->objectMap[$this->currentReaderId][$value->objectNumber];
$this->_newobj($n); $this->_newobj($n);
$this->writePdfType($value->value); $this->writePdfType($value->value);
// add newline before "endobj" for all objects in view to PDF/A conformance
if (
!(
($value->value instanceof PdfArray) ||
($value->value instanceof PdfDictionary) ||
($value->value instanceof PdfToken) ||
($value->value instanceof PdfStream)
)
) {
$this->_put("\n", false);
}
$this->_put('endobj'); $this->_put('endobj');
} }
} }

View file

@ -0,0 +1,97 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\Math\Matrix;
use setasign\Fpdi\Math\Vector;
/**
* A simple graphic state class which holds the current transformation matrix.
*/
class GraphicsState
{
/**
* @var Matrix
*/
protected $ctm;
/**
* @param Matrix|null $ctm
*/
public function __construct(Matrix $ctm = null)
{
if ($ctm === null) {
$ctm = new Matrix();
}
$this->ctm = $ctm;
}
/**
* @param Matrix $matrix
* @return $this
*/
public function add(Matrix $matrix)
{
$this->ctm = $matrix->multiply($this->ctm);
return $this;
}
/**
* @param int|float $x
* @param int|float $y
* @param int|float $angle
* @return $this
*/
public function rotate($x, $y, $angle)
{
if (abs($angle) < 1e-5) {
return $this;
}
$angle = deg2rad($angle);
$c = cos($angle);
$s = sin($angle);
$this->add(new Matrix($c, $s, -$s, $c, $x, $y));
return $this->translate(-$x, -$y);
}
/**
* @param int|float $shiftX
* @param int|float $shiftY
* @return $this
*/
public function translate($shiftX, $shiftY)
{
return $this->add(new Matrix(1, 0, 0, 1, $shiftX, $shiftY));
}
/**
* @param int|float $scaleX
* @param int|float $scaleY
* @return $this
*/
public function scale($scaleX, $scaleY)
{
return $this->add(new Matrix($scaleX, 0, 0, $scaleY, 0, 0));
}
/**
* @param Vector $vector
* @return Vector
*/
public function toUserSpace(Vector $vector)
{
return $vector->multiplyWithMatrix($this->ctm);
}
}

View file

@ -0,0 +1,116 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\Math;
/**
* A simple 2D-Matrix class
*/
class Matrix
{
/**
* @var float
*/
protected $a;
/**
* @var float
*/
protected $b;
/**
* @var float
*/
protected $c;
/**
* @var float
*/
protected $d;
/**
* @var float
*/
protected $e;
/**
* @var float
*/
protected $f;
/**
* @param int|float $a
* @param int|float $b
* @param int|float $c
* @param int|float $d
* @param int|float $e
* @param int|float $f
*/
public function __construct($a = 1, $b = 0, $c = 0, $d = 1, $e = 0, $f = 0)
{
$this->a = (float)$a;
$this->b = (float)$b;
$this->c = (float)$c;
$this->d = (float)$d;
$this->e = (float)$e;
$this->f = (float)$f;
}
/**
* @return float[]
*/
public function getValues()
{
return [$this->a, $this->b, $this->c, $this->d, $this->e, $this->f];
}
/**
* @param Matrix $by
* @return Matrix
*/
public function multiply(self $by)
{
$a =
$this->a * $by->a
+ $this->b * $by->c
//+ 0 * $by->e
;
$b =
$this->a * $by->b
+ $this->b * $by->d
//+ 0 * $by->f
;
$c =
$this->c * $by->a
+ $this->d * $by->c
//+ 0 * $by->e
;
$d =
$this->c * $by->b
+ $this->d * $by->d
//+ 0 * $by->f
;
$e =
$this->e * $by->a
+ $this->f * $by->c
+ /*1 * */$by->e;
$f =
$this->e * $by->b
+ $this->f * $by->d
+ /*1 * */$by->f;
return new self($a, $b, $c, $d, $e, $f);
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\Math;
/**
* A simple 2D-Vector class
*/
class Vector
{
/**
* @var float
*/
protected $x;
/**
* @var float
*/
protected $y;
/**
* @param int|float $x
* @param int|float $y
*/
public function __construct($x = .0, $y = .0)
{
$this->x = (float)$x;
$this->y = (float)$y;
}
/**
* @return float
*/
public function getX()
{
return $this->x;
}
/**
* @return float
*/
public function getY()
{
return $this->y;
}
/**
* @param Matrix $matrix
* @return Vector
*/
public function multiplyWithMatrix(Matrix $matrix)
{
list($a, $b, $c, $d, $e, $f) = $matrix->getValues();
$x = $a * $this->x + $c * $this->y + $e;
$y = $b * $this->x + $d * $this->y + $f;
return new self($x, $y);
}
}

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -69,7 +69,7 @@ class CrossReference
// sometimes the file header offset is part of the byte offsets, so let's retry by resetting it to zero. // sometimes the file header offset is part of the byte offsets, so let's retry by resetting it to zero.
if ($e->getCode() === CrossReferenceException::INVALID_DATA && $this->fileHeaderOffset !== 0) { if ($e->getCode() === CrossReferenceException::INVALID_DATA && $this->fileHeaderOffset !== 0) {
$this->fileHeaderOffset = 0; $this->fileHeaderOffset = 0;
$reader = $this->readXref($offset + $this->fileHeaderOffset); $reader = $this->readXref($offset);
} else { } else {
throw $e; throw $e;
} }

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -44,7 +44,9 @@ class Flate implements FilterInterface
// let's try if the checksum is CRC32 // let's try if the checksum is CRC32
$fh = fopen('php://temp', 'w+b'); $fh = fopen('php://temp', 'w+b');
fwrite($fh, "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $oData); fwrite($fh, "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $oData);
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 30]); // "window" == 31 -> 16 + (8 to 15): Uses the low 4 bits of the value as the window size logarithm.
// The input must include a gzip header and trailer (via 16).
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 31]);
fseek($fh, 0); fseek($fh, 0);
$data = @stream_get_contents($fh); $data = @stream_get_contents($fh);
fclose($fh); fclose($fh);
@ -53,21 +55,10 @@ class Flate implements FilterInterface
return $data; return $data;
} }
// Try this fallback // Try this fallback (remove the zlib stream header)
$tries = 0; $data = @(gzinflate(substr($oData, 2)));
$oDataLen = strlen($oData); if ($data === false) {
while ($tries < 6 && ($data === false || (strlen($data) < ($oDataLen - $tries - 1)))) {
$data = @(gzinflate(substr($oData, $tries)));
$tries++;
}
// let's use this fallback only if the $data is longer than the original data
if (strlen($data) > ($oDataLen - $tries - 1)) {
return $data;
}
if (!$data) {
throw new FlateException( throw new FlateException(
'Error while decompressing stream.', 'Error while decompressing stream.',
FlateException::DECOMPRESS_ERROR FlateException::DECOMPRESS_ERROR

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -25,6 +25,7 @@ use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfString; use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfToken; use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfType; use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/** /**
* A PDF parser class * A PDF parser class
@ -145,7 +146,7 @@ class PdfParser
} }
/** /**
* Get the cross reference instance. * Get the cross-reference instance.
* *
* @return CrossReference * @return CrossReference
* @throws CrossReferenceException * @throws CrossReferenceException
@ -258,25 +259,25 @@ class PdfParser
switch ($token) { switch ($token) {
case '(': case '(':
$this->ensureExpectedType($token, $expectedType); $this->ensureExpectedType($token, $expectedType);
return PdfString::parse($this->streamReader); return $this->parsePdfString();
case '<': case '<':
if ($this->streamReader->getByte() === '<') { if ($this->streamReader->getByte() === '<') {
$this->ensureExpectedType('<<', $expectedType); $this->ensureExpectedType('<<', $expectedType);
$this->streamReader->addOffset(1); $this->streamReader->addOffset(1);
return PdfDictionary::parse($this->tokenizer, $this->streamReader, $this); return $this->parsePdfDictionary();
} }
$this->ensureExpectedType($token, $expectedType); $this->ensureExpectedType($token, $expectedType);
return PdfHexString::parse($this->streamReader); return $this->parsePdfHexString();
case '/': case '/':
$this->ensureExpectedType($token, $expectedType); $this->ensureExpectedType($token, $expectedType);
return PdfName::parse($this->tokenizer, $this->streamReader); return $this->parsePdfName();
case '[': case '[':
$this->ensureExpectedType($token, $expectedType); $this->ensureExpectedType($token, $expectedType);
return PdfArray::parse($this->tokenizer, $this); return $this->parsePdfArray();
default: default:
if (\is_numeric($token)) { if (\is_numeric($token)) {
@ -291,13 +292,7 @@ class PdfParser
); );
} }
return PdfIndirectObject::parse( return $this->parsePdfIndirectObject((int)$token, (int)$token2);
(int) $token,
(int) $token2,
$this,
$this->tokenizer,
$this->streamReader
);
case 'R': case 'R':
if ( if (
$expectedType !== null && $expectedType !== null &&
@ -309,7 +304,7 @@ class PdfParser
); );
} }
return PdfIndirectObjectReference::create((int) $token, (int) $token2); return PdfIndirectObjectReference::create((int)$token, (int)$token2);
} }
$this->tokenizer->pushStack($token3); $this->tokenizer->pushStack($token3);
@ -351,6 +346,65 @@ class PdfParser
} }
} }
/**
* @return PdfString
*/
protected function parsePdfString()
{
return PdfString::parse($this->streamReader);
}
/**
* @return false|PdfHexString
*/
protected function parsePdfHexString()
{
return PdfHexString::parse($this->streamReader);
}
/**
* @return bool|PdfDictionary
* @throws PdfTypeException
*/
protected function parsePdfDictionary()
{
return PdfDictionary::parse($this->tokenizer, $this->streamReader, $this);
}
/**
* @return PdfName
*/
protected function parsePdfName()
{
return PdfName::parse($this->tokenizer, $this->streamReader);
}
/**
* @return false|PdfArray
* @throws PdfTypeException
*/
protected function parsePdfArray()
{
return PdfArray::parse($this->tokenizer, $this);
}
/**
* @param int $objectNumber
* @param int $generationNumber
* @return false|PdfIndirectObject
* @throws Type\PdfTypeException
*/
protected function parsePdfIndirectObject($objectNumber, $generationNumber)
{
return PdfIndirectObject::parse(
$objectNumber,
$generationNumber,
$this,
$this->tokenizer,
$this->streamReader
);
}
/** /**
* Ensures that the token will evaluate to an expected object type (or not). * Ensures that the token will evaluate to an expected object type (or not).
* *
@ -359,7 +413,7 @@ class PdfParser
* @return bool * @return bool
* @throws Type\PdfTypeException * @throws Type\PdfTypeException
*/ */
private function ensureExpectedType($token, $expectedType) protected function ensureExpectedType($token, $expectedType)
{ {
static $mapping = [ static $mapping = [
'(' => PdfString::class, '(' => PdfString::class,

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -113,6 +113,12 @@ class StreamReader
); );
} }
if (fseek($stream, 0) === -1) {
throw new \InvalidArgumentException(
'Given stream is not seekable!'
);
}
$this->stream = $stream; $this->stream = $stream;
$this->closeStream = $closeStream; $this->closeStream = $closeStream;
$this->reset(); $this->reset();

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -25,7 +25,7 @@ class PdfArray extends PdfType
* *
* @param Tokenizer $tokenizer * @param Tokenizer $tokenizer
* @param PdfParser $parser * @param PdfParser $parser
* @return bool|self * @return false|self
* @throws PdfTypeException * @throws PdfTypeException
*/ */
public static function parse(Tokenizer $tokenizer, PdfParser $parser) public static function parse(Tokenizer $tokenizer, PdfParser $parser)

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -21,7 +21,7 @@ class PdfHexString extends PdfType
* Parses a hexadecimal string object from the stream reader. * Parses a hexadecimal string object from the stream reader.
* *
* @param StreamReader $streamReader * @param StreamReader $streamReader
* @return bool|self * @return false|self
*/ */
public static function parse(StreamReader $streamReader) public static function parse(StreamReader $streamReader)
{ {

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -22,17 +22,17 @@ class PdfIndirectObject extends PdfType
/** /**
* Parses an indirect object from a tokenizer, parser and stream-reader. * Parses an indirect object from a tokenizer, parser and stream-reader.
* *
* @param int $objectNumberToken * @param int $objectNumber
* @param int $objectGenerationNumberToken * @param int $objectGenerationNumber
* @param PdfParser $parser * @param PdfParser $parser
* @param Tokenizer $tokenizer * @param Tokenizer $tokenizer
* @param StreamReader $reader * @param StreamReader $reader
* @return bool|self * @return self|false
* @throws PdfTypeException * @throws PdfTypeException
*/ */
public static function parse( public static function parse(
$objectNumberToken, $objectNumber,
$objectGenerationNumberToken, $objectGenerationNumber,
PdfParser $parser, PdfParser $parser,
Tokenizer $tokenizer, Tokenizer $tokenizer,
StreamReader $reader StreamReader $reader
@ -50,8 +50,8 @@ class PdfIndirectObject extends PdfType
} }
$v = new self(); $v = new self();
$v->objectNumber = (int) $objectNumberToken; $v->objectNumber = (int) $objectNumber;
$v->generationNumber = (int) $objectGenerationNumberToken; $v->generationNumber = (int) $objectGenerationNumber;
$v->value = $value; $v->value = $value;
return $v; return $v;

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -212,6 +212,28 @@ class PdfStream extends PdfType
return $buffer; return $buffer;
} }
/**
* Get all filters defined for this stream.
*
* @return PdfType[]
* @throws PdfTypeException
*/
public function getFilters()
{
$filters = PdfDictionary::get($this->value, 'Filter');
if ($filters instanceof PdfNull) {
return [];
}
if ($filters instanceof PdfArray) {
$filters = $filters->value;
} else {
$filters = [$filters];
}
return $filters;
}
/** /**
* Get the unfiltered stream data. * Get the unfiltered stream data.
* *
@ -222,17 +244,11 @@ class PdfStream extends PdfType
public function getUnfilteredStream() public function getUnfilteredStream()
{ {
$stream = $this->getStream(); $stream = $this->getStream();
$filters = PdfDictionary::get($this->value, 'Filter'); $filters = $this->getFilters();
if ($filters instanceof PdfNull) { if ($filters === []) {
return $stream; return $stream;
} }
if ($filters instanceof PdfArray) {
$filters = $filters->value;
} else {
$filters = [$filters];
}
$decodeParams = PdfDictionary::get($this->value, 'DecodeParms'); $decodeParams = PdfDictionary::get($this->value, 'DecodeParms');
if ($decodeParams instanceof PdfArray) { if ($decodeParams instanceof PdfArray) {
$decodeParams = $decodeParams->value; $decodeParams = $decodeParams->value;
@ -308,6 +324,21 @@ class PdfStream extends PdfType
$stream = $filterObject->decode($stream); $stream = $filterObject->decode($stream);
break; break;
case 'Crypt':
if (!$decodeParam instanceof PdfDictionary) {
break;
}
// Filter is "Identity"
$name = PdfDictionary::get($decodeParam, 'Name');
if (!$name instanceof PdfName || $name->value !== 'Identity') {
break;
}
throw new FilterException(
'Support for Crypt filters other than "Identity" is not implemented.',
FilterException::UNSUPPORTED_FILTER
);
default: default:
throw new FilterException( throw new FilterException(
\sprintf('Unsupported filter "%s".', $filter->value), \sprintf('Unsupported filter "%s".', $filter->value),

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -78,6 +78,36 @@ class PdfString extends PdfType
return PdfType::ensureType(self::class, $string, 'String value expected.'); return PdfType::ensureType(self::class, $string, 'String value expected.');
} }
/**
* Escapes sequences in a string according to the PDF specification.
*
* @param string $s
* @return string
*/
public static function escape($s)
{
// Still a bit faster, than direct replacing
if (
\strpos($s, '\\') !== false ||
\strpos($s, ')') !== false ||
\strpos($s, '(') !== false ||
\strpos($s, "\x0D") !== false ||
\strpos($s, "\x0A") !== false ||
\strpos($s, "\x09") !== false ||
\strpos($s, "\x08") !== false ||
\strpos($s, "\x0C") !== false
) {
// is faster than strtr(...)
return \str_replace(
['\\', ')', '(', "\x0D", "\x0A", "\x09", "\x08", "\x0C"],
['\\\\', '\\)', '\\(', '\r', '\n', '\t', '\b', '\f'],
$s
);
}
return $s;
}
/** /**
* Unescapes escaped sequences in a PDF string according to the PDF specification. * Unescapes escaped sequences in a PDF string according to the PDF specification.
* *

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
@ -69,6 +69,34 @@ class PdfType
return $value; return $value;
} }
/**
* Flatten indirect object references to direct objects.
*
* @param PdfType $value
* @param PdfParser $parser
* @return PdfType
* @throws CrossReferenceException
* @throws PdfParserException
*/
public static function flatten(PdfType $value, PdfParser $parser)
{
if ($value instanceof PdfIndirectObjectReference) {
return self::flatten(self::resolve($value, $parser), $parser);
}
if ($value instanceof PdfDictionary || $value instanceof PdfArray) {
foreach ($value->value as $key => $_value) {
$value->value[$key] = self::flatten($_value, $parser);
}
}
if ($value instanceof PdfStream) {
throw new PdfTypeException('There is a stream object found which cannot be flattened to a direct object.');
}
return $value;
}
/** /**
* The value of the PDF type. * The value of the PDF type.
* *

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,12 +4,13 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
namespace setasign\Fpdi\PdfReader\DataStructure; namespace setasign\Fpdi\PdfReader\DataStructure;
use setasign\Fpdi\Math\Vector;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException; use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParser; use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException; use setasign\Fpdi\PdfParser\PdfParserException;
@ -64,6 +65,11 @@ class Rectangle
return new self($ax, $ay, $bx, $by); return new self($ax, $ay, $bx, $by);
} }
public static function byVectors(Vector $ll, Vector $ur)
{
return new self($ll->getX(), $ll->getY(), $ur->getX(), $ur->getY());
}
/** /**
* Rectangle constructor. * Rectangle constructor.
* *

View file

@ -4,21 +4,27 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
namespace setasign\Fpdi\PdfReader; namespace setasign\Fpdi\PdfReader;
use setasign\Fpdi\FpdiException;
use setasign\Fpdi\GraphicsState;
use setasign\Fpdi\Math\Vector;
use setasign\Fpdi\PdfParser\Filter\FilterException; use setasign\Fpdi\PdfParser\Filter\FilterException;
use setasign\Fpdi\PdfParser\PdfParser; use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException; use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfArray; use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfDictionary; use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfHexString;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject; use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfName;
use setasign\Fpdi\PdfParser\Type\PdfNull; use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfNumeric; use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream; use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfType; use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException; use setasign\Fpdi\PdfParser\Type\PdfTypeException;
use setasign\Fpdi\PdfReader\DataStructure\Rectangle; use setasign\Fpdi\PdfReader\DataStructure\Rectangle;
@ -83,7 +89,7 @@ class Page
*/ */
public function getPageDictionary() public function getPageDictionary()
{ {
if (null === $this->pageDictionary) { if ($this->pageDictionary === null) {
$this->pageDictionary = PdfDictionary::ensure(PdfType::resolve($this->getPageObject(), $this->parser)); $this->pageDictionary = PdfDictionary::ensure(PdfType::resolve($this->getPageObject(), $this->parser));
} }
@ -155,7 +161,7 @@ class Page
public function getRotation() public function getRotation()
{ {
$rotation = $this->getAttribute('Rotate'); $rotation = $this->getAttribute('Rotate');
if (null === $rotation) { if ($rotation === null) {
return 0; return 0;
} }
@ -268,4 +274,147 @@ class Page
PdfReaderException::UNEXPECTED_DATA_TYPE PdfReaderException::UNEXPECTED_DATA_TYPE
); );
} }
/**
* Get information of all external links on this page.
*
* All coordinates are normalized in view to rotation and translation of the boundary-box, so that their
* origin is lower-left.
*
* @return array
*/
public function getExternalLinks($box = PageBoundaries::CROP_BOX)
{
try {
$dict = $this->getPageDictionary();
$annotations = PdfType::resolve(PdfDictionary::get($dict, 'Annots'), $this->parser);
} catch (FpdiException $e) {
return [];
}
if (!$annotations instanceof PdfArray) {
return [];
}
$links = [];
foreach ($annotations->value as $entry) {
try {
$annotation = PdfType::resolve($entry, $this->parser);
$value = PdfType::resolve(PdfDictionary::get($annotation, 'Subtype'), $this->parser);
if (!$value instanceof PdfName || $value->value !== 'Link') {
continue;
}
$dest = PdfType::resolve(PdfDictionary::get($annotation, 'Dest'), $this->parser);
if (!$dest instanceof PdfNull) {
continue;
}
$action = PdfType::resolve(PdfDictionary::get($annotation, 'A'), $this->parser);
if (!$action instanceof PdfDictionary) {
continue;
}
$actionType = PdfType::resolve(PdfDictionary::get($action, 'S'), $this->parser);
if (!$actionType instanceof PdfName || $actionType->value !== 'URI') {
continue;
}
$uri = PdfType::resolve(PdfDictionary::get($action, 'URI'), $this->parser);
if ($uri instanceof PdfString) {
$uriValue = PdfString::unescape($uri->value);
} elseif ($uri instanceof PdfHexString) {
$uriValue = \hex2bin($uri->value);
} else {
continue;
}
$rect = PdfType::resolve(PdfDictionary::get($annotation, 'Rect'), $this->parser);
if (!$rect instanceof PdfArray || count($rect->value) !== 4) {
continue;
}
$rect = Rectangle::byPdfArray($rect, $this->parser);
if ($rect->getWidth() === 0 || $rect->getHeight() === 0) {
continue;
}
$bbox = $this->getBoundary($box);
$rotation = $this->getRotation();
$gs = new GraphicsState();
$gs->translate(-$bbox->getLlx(), -$bbox->getLly());
$gs->rotate($bbox->getLlx(), $bbox->getLly(), -$rotation);
switch ($rotation) {
case 90:
$gs->translate(-$bbox->getWidth(), 0);
break;
case 180:
$gs->translate(-$bbox->getWidth(), -$bbox->getHeight());
break;
case 270:
$gs->translate(0, -$bbox->getHeight());
break;
}
$normalizedRect = Rectangle::byVectors(
$gs->toUserSpace(new Vector($rect->getLlx(), $rect->getLly())),
$gs->toUserSpace(new Vector($rect->getUrx(), $rect->getUry()))
);
$quadPoints = PdfType::resolve(PdfDictionary::get($annotation, 'QuadPoints'), $this->parser);
$normalizedQuadPoints = [];
if ($quadPoints instanceof PdfArray) {
$quadPointsCount = count($quadPoints->value);
if ($quadPointsCount % 8 === 0) {
for ($i = 0; ($i + 1) < $quadPointsCount; $i += 2) {
$x = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i], $this->parser));
$y = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i + 1], $this->parser));
$v = $gs->toUserSpace(new Vector($x->value, $y->value));
$normalizedQuadPoints[] = $v->getX();
$normalizedQuadPoints[] = $v->getY();
}
}
}
// we remove unsupported/unneeded values here
unset(
$annotation->value['P'],
$annotation->value['NM'],
$annotation->value['AP'],
$annotation->value['AS'],
$annotation->value['Type'],
$annotation->value['Subtype'],
$annotation->value['Rect'],
$annotation->value['A'],
$annotation->value['QuadPoints'],
$annotation->value['Rotate'],
$annotation->value['M'],
$annotation->value['StructParent'],
$annotation->value['OC']
);
// ...and flatten the PDF object to eliminate any indirect references.
// Indirect references are a problem when writing the output in FPDF
// because FPDF uses pre-calculated object numbers while FPDI creates
// them at runtime.
$annotation = PdfType::flatten($annotation, $this->parser);
$links[] = [
'rect' => $normalizedRect,
'quadPoints' => $normalizedQuadPoints,
'uri' => $uriValue,
'pdfObject' => $annotation
];
} catch (FpdiException $e) {
continue;
}
}
return $links;
}
} }

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,18 +4,22 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
namespace setasign\Fpdi\Tcpdf; namespace setasign\Fpdi\Tcpdf;
use setasign\Fpdi\FpdiException;
use setasign\Fpdi\FpdiTrait; use setasign\Fpdi\FpdiTrait;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException; use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\Filter\AsciiHex; use setasign\Fpdi\PdfParser\Filter\AsciiHex;
use setasign\Fpdi\PdfParser\PdfParserException; use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfHexString; use setasign\Fpdi\PdfParser\Type\PdfHexString;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject; use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfName;
use setasign\Fpdi\PdfParser\Type\PdfNull; use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfNumeric; use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream; use setasign\Fpdi\PdfParser\Type\PdfStream;
@ -42,7 +46,7 @@ class Fpdi extends \pdf
* *
* @string * @string
*/ */
const VERSION = '2.3.7'; const VERSION = '2.6.0';
/** /**
* A counter for template ids. * A counter for template ids.
@ -146,7 +150,7 @@ class Fpdi extends \pdf
{ {
$out = parent::_getxobjectdict(); $out = parent::_getxobjectdict();
foreach ($this->importedPages as $key => $pageData) { foreach ($this->importedPages as $pageData) {
$out .= '/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R '; $out .= '/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R ';
} }
@ -247,7 +251,7 @@ class Fpdi extends \pdf
if ($value instanceof PdfString) { if ($value instanceof PdfString) {
$string = PdfString::unescape($value->value); $string = PdfString::unescape($value->value);
$string = $this->_encrypt_data($this->currentObjectNumber, $string); $string = $this->_encrypt_data($this->currentObjectNumber, $string);
$value->value = \TCPDF_STATIC::_escape($string); $value->value = PdfString::escape($string);
} elseif ($value instanceof PdfHexString) { } elseif ($value instanceof PdfHexString) {
$filter = new AsciiHex(); $filter = new AsciiHex();
$string = $filter->decode($value->value); $string = $filter->decode($value->value);
@ -268,4 +272,120 @@ class Fpdi extends \pdf
$this->fpdiWritePdfType($value); $this->fpdiWritePdfType($value);
} }
/**
* This method will add additional data to the last created link/annotation.
*
* It will copy styling properties (supported by TCPDF) of the imported link.
*
* @param array $externalLink
* @param float|int $xPt
* @param float|int $scaleX
* @param float|int $yPt
* @param float|int $newHeightPt
* @param float|int $scaleY
* @param array $importedPage
* @return void
*/
protected function adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage)
{
$parser = $this->getPdfReader($importedPage['readerId'])->getParser();
if ($this->inxobj) {
// store parameters for later use on template
$lastAnnotationKey = count($this->xobjects[$this->xobjid]['annotations']) - 1;
$lastAnnotationOpt = &$this->xobjects[$this->xobjid]['annotations'][$lastAnnotationKey]['opt'];
} else {
$lastAnnotationKey = count($this->PageAnnots[$this->page]) - 1;
$lastAnnotationOpt = &$this->PageAnnots[$this->page][$lastAnnotationKey]['opt'];
}
// ensure we have a default value - otherwise TCPDF will set it to 4 throughout
$lastAnnotationOpt['f'] = 0;
// values in this dictonary are all direct objects and we don't need to resolve them here again.
$values = $externalLink['pdfObject']->value;
foreach ($values as $key => $value) {
try {
switch ($key) {
case 'BS':
$value = PdfDictionary::ensure($value);
$bs = [];
if (isset($value->value['W'])) {
$bs['w'] = PdfNumeric::ensure($value->value['W'])->value;
}
if (isset($value->value['S'])) {
$bs['s'] = PdfName::ensure($value->value['S'])->value;
}
if (isset($value->value['D'])) {
$d = [];
foreach (PdfArray::ensure($value->value['D'])->value as $item) {
$d[] = PdfNumeric::ensure($item)->value;
}
$bs['d'] = $d;
}
$lastAnnotationOpt['bs'] = $bs;
break;
case 'Border':
$borderArray = PdfArray::ensure($value)->value;
if (count($borderArray) < 3) {
continue 2;
}
$border = [
PdfNumeric::ensure($borderArray[0])->value,
PdfNumeric::ensure($borderArray[1])->value,
PdfNumeric::ensure($borderArray[2])->value,
];
if (isset($borderArray[3])) {
$dashArray = [];
foreach (PdfArray::ensure($borderArray[3])->value as $item) {
$dashArray[] = PdfNumeric::ensure($item)->value;
}
$border[] = $dashArray;
}
$lastAnnotationOpt['border'] = $border;
break;
case 'C':
$c = [];
$colors = PdfArray::ensure(PdfType::resolve($value, $parser))->value;
$m = count($colors) === 4 ? 100 : 255;
foreach ($colors as $item) {
$c[] = PdfNumeric::ensure($item)->value * $m;
}
$lastAnnotationOpt['c'] = $c;
break;
case 'F':
$lastAnnotationOpt['f'] = $value->value;
break;
case 'BE':
// is broken in current TCPDF version: "bc" key is checked but "bs" is used.
break;
}
// let's silence invalid/not supported values
} catch (FpdiException $e) {
continue;
}
}
// QuadPoints are not supported by TCPDF
// if (count($externalLink['quadPoints']) > 0) {
// $quadPoints = [];
// for ($i = 0, $n = count($externalLink['quadPoints']); $i < $n; $i += 2) {
// $quadPoints[] = $xPt + $externalLink['quadPoints'][$i] * $scaleX;
// $quadPoints[] = $this->hPt - $yPt - $newHeightPt + $externalLink['quadPoints'][$i + 1] * $scaleY;
// }
//
// ????? = $quadPoints;
// }
}
} }

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -4,17 +4,14 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */
namespace setasign\Fpdi\Tfpdf; namespace setasign\Fpdi\Tfpdf;
use setasign\Fpdi\FpdfTrait;
use setasign\Fpdi\FpdiTrait; use setasign\Fpdi\FpdiTrait;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfNull;
/** /**
* Class Fpdi * Class Fpdi
@ -24,131 +21,12 @@ use setasign\Fpdi\PdfParser\Type\PdfNull;
class Fpdi extends FpdfTpl class Fpdi extends FpdfTpl
{ {
use FpdiTrait; use FpdiTrait;
use FpdfTrait;
/** /**
* FPDI version * FPDI version
* *
* @string * @string
*/ */
const VERSION = '2.3.7'; const VERSION = '2.6.0';
public function _enddoc()
{
parent::_enddoc();
$this->cleanUp();
}
/**
* Draws an imported page or a template onto the page or another template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size
* @see Fpdi::getTemplateSize()
*/
public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (isset($this->importedPages[$tpl])) {
$size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
if ($this->currentTemplateId !== null) {
$this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl;
}
return $size;
}
return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize);
}
/**
* Get the size of an imported page or template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getTemplateSize($tpl, $width = null, $height = null)
{
$size = parent::getTemplateSize($tpl, $width, $height);
if ($size === false) {
return $this->getImportedPageSize($tpl, $width, $height);
}
return $size;
}
/**
* @inheritdoc
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function _putimages()
{
$this->currentReaderId = null;
parent::_putimages();
foreach ($this->importedPages as $key => $pageData) {
$this->_newobj();
$this->importedPages[$key]['objectNumber'] = $this->n;
$this->currentReaderId = $pageData['readerId'];
$this->writePdfType($pageData['stream']);
$this->_put('endobj');
}
foreach (\array_keys($this->readers) as $readerId) {
$parser = $this->getPdfReader($readerId)->getParser();
$this->currentReaderId = $readerId;
while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
try {
$object = $parser->getIndirectObject($objectNumber);
} catch (CrossReferenceException $e) {
if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
$object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
} else {
throw $e;
}
}
$this->writePdfType($object);
}
}
$this->currentReaderId = null;
}
/**
* @inheritdoc
*/
protected function _putxobjectdict()
{
foreach ($this->importedPages as $key => $pageData) {
$this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R');
}
parent::_putxobjectdict();
}
/**
* @inheritdoc
*/
protected function _put($s, $newLine = true)
{
if ($newLine) {
$this->buffer .= $s . "\n";
} else {
$this->buffer .= $s;
}
}
} }

View file

@ -4,7 +4,7 @@
* This file is part of FPDI * This file is part of FPDI
* *
* @package setasign\Fpdi * @package setasign\Fpdi
* @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) * @copyright Copyright (c) 2023 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License * @license http://opensource.org/licenses/mit-license The MIT License
*/ */

View file

@ -17,67 +17,3 @@ Installation
1) Download the latest version of fpdi from the url above. 1) Download the latest version of fpdi from the url above.
2) Unzip the src directory files into this directory. 2) Unzip the src directory files into this directory.
3) Update mod/assign/feedback/editpdf/fpdi/Tcpdf/Fpdi.php(or whichever file it has been replaced with) to extend 'pdf' instead of 'TCPDF'. 3) Update mod/assign/feedback/editpdf/fpdi/Tcpdf/Fpdi.php(or whichever file it has been replaced with) to extend 'pdf' instead of 'TCPDF'.
4) Make a note below of any changes made.
2021/05/26
----------
1/ Updated to 2.3.6
Updated by Paul Holden (MDL-71717)
2020/12/16
----------
1/ Updated to 2.3.5
Updated by Paul Holden (MDL-70312)
2019/06/18
----------
1/ Updated to 2.2.0
2/ Amended the installation instructions
3/ Updated pdf.php to inherit from the TCPDF version instead of the FPDI generic version
(Refer https://manuals.setasign.com/fpdi-manual/v2/migrating/#index-4)
4/ Updated pdf.php to account for function changes
(Refer https://manuals.setasign.com/fpdi-manual/v2/migrating/#index-6)
5/ With the restructure by fpdi, no need to apply patches from MDL-55848 and MDL-60301
Updated by Peter Dias (MDL-65774)
2017/10/03
----------
1/ Updated to 1.6.2
2/ Cherry-picked changes in MDL-55848.
3/ Renamed 'TCPDF' to 'pdf' as stated above.
4/ Applied php7.2 compatibility fix, remove this if it is fixed in next major release (2.0) (MDL-60301).
Updated by Ankit Agarwal<ankit.agrr@gmail.com> (MDL-60301)
2016/11/15
----------
1) Class not exists check and the empty fpdi_bridge class has been removed from fpdi_bridge.php to fix a behat error.
Updated by Simey Lameze (MDL-55848)
2015/12/04
----------
Updated to FPDI: 1.6.1
1) Changed 'TCPDF' to 'pdf' (as stated above).
2) License changed from Apache Software License 2.0 to The MIT License (MIT).
2015/10/01
----------
Updated to FPDI: 1.5.4
fpdi no longer uses fpdi2tcpdf_bridge.php this has been replaced with fpdi_bridge.php.
fpdi_bridge.php has been modified to extend pdf (lib/pdflib.php) as was done with
fpdi2tcpdf_bridge.php.
Updated by Adrian Greeve (MDL-49515)
------------------
Previous versions:
FPDI: 1.4.4
FPDF_TPL: 1.2.3

View file

@ -4,7 +4,7 @@
<location>fpdi</location> <location>fpdi</location>
<name>FPDI</name> <name>FPDI</name>
<description>Collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF.</description> <description>Collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF.</description>
<version>2.3.7</version> <version>2.6.0</version>
<license>MIT</license> <license>MIT</license>
<repository>https://github.com/Setasign/FPDI</repository> <repository>https://github.com/Setasign/FPDI</repository>
<copyrights> <copyrights>