From 967cd0e6d4393d8b637c896751c96a4d074a1983 Mon Sep 17 00:00:00 2001 From: Nicola Asuni Date: Sat, 17 Aug 2024 08:49:09 +0100 Subject: [PATCH] Draft xobject support + refactor bbox (#111) --- VERSION | 2 +- composer.json | 2 +- examples/index.php | 11 +- resources/debian/control | 2 +- resources/rpm/rpm.spec | 40 +++---- src/Base.php | 155 ++++++++++++++---------- src/ClassObjects.php | 42 ++++--- src/Output.php | 151 +++++++++++++---------- src/Tcpdf.php | 252 ++++++++++++++++++++++++++++++++++----- src/Text.php | 43 +++---- 10 files changed, 468 insertions(+), 232 deletions(-) diff --git a/VERSION b/VERSION index fd89226..080c95b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.66 +8.0.68 diff --git a/composer.json b/composer.json index a12dc9d..2db05d2 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "tecnickcom/tc-lib-barcode": "^2.2", "tecnickcom/tc-lib-color": "^2.0", "tecnickcom/tc-lib-pdf-image": "^2.0", - "tecnickcom/tc-lib-pdf-font": "^2.2", + "tecnickcom/tc-lib-pdf-font": "^2.3", "tecnickcom/tc-lib-file": "^2.0", "tecnickcom/tc-lib-pdf-encrypt": "^2.1", "tecnickcom/tc-lib-unicode-data": "^2.0", diff --git a/examples/index.php b/examples/index.php index d19cb29..dea8311 100644 --- a/examples/index.php +++ b/examples/index.php @@ -1026,7 +1026,6 @@ $pdf->graph->add($styletxt); - $bfont2 = $pdf->font->insert($pdf->pon, 'times', 'BI', 24); $pdf->page->addContent($bfont2['out']); @@ -1043,13 +1042,13 @@ $pdf->page->addContent($txt); -$bbox = $pdf->getLastTextBBox(); +$bbox = $pdf->getLastBBox(); // Add text $txt2 = $pdf->getTextLine( 'Link to https://tcpdf.org', 15, - ($bbox['y'] + $bbox['height'] + $pdf->toUnit($bfont2['ascent'])), + ($bbox['y'] + $bbox['h'] + $pdf->toUnit($bfont2['ascent'])), 0, 0, 0, @@ -1071,13 +1070,13 @@ $pdf->page->addContent($txt2); // get the coordinates of the box containing the last added text string. -$bbox = $pdf->getLastTextBBox(); +$bbox = $pdf->getLastBBox(); $aoid = $pdf->setAnnotation( $bbox['x'], $bbox['y'], - $bbox['width'], - $bbox['height'], + $bbox['w'], + $bbox['h'], 'https://tcpdf.org', [ 'subtype' => 'Link', diff --git a/resources/debian/control b/resources/debian/control index ca71eed..8555829 100644 --- a/resources/debian/control +++ b/resources/debian/control @@ -10,6 +10,6 @@ Vcs-Git: https://github.com/~#VENDOR#~/~#PROJECT#~.git Package: ~#PKGNAME#~ Provides: php-~#PROJECT#~ Architecture: all -Depends: php (>= 8.0.0), php-date, php-tecnickcom-tc-lib-barcode (<< 2.0.0), php-tecnickcom-tc-lib-barcode (>= 2.2.1), php-tecnickcom-tc-lib-color (<< 2.0.0), php-tecnickcom-tc-lib-color (>= 2.0.8), php-tecnickcom-tc-lib-pdf-image (<< 2.0.0), php-tecnickcom-tc-lib-pdf-image (>= 2.0.12), php-tecnickcom-tc-lib-pdf-font (<< 2.0.0), php-tecnickcom-tc-lib-pdf-font (>= 2.2.3), php-tecnickcom-tc-lib-file (<< 2.0.0), php-tecnickcom-tc-lib-file (>= 2.0.11), php-tecnickcom-tc-lib-pdf-encrypt (<< 2.0.0), php-tecnickcom-tc-lib-pdf-encrypt (>= 2.1.2), php-tecnickcom-tc-lib-unicode-data (<< 2.0.0), php-tecnickcom-tc-lib-unicode-data (>= 2.0.11), php-tecnickcom-tc-lib-unicode (<< 2.0.0), php-tecnickcom-tc-lib-unicode (>= 2.0.11), php-tecnickcom-tc-lib-pdf-page (<< 3.0.0), php-tecnickcom-tc-lib-pdf-page (>= 4.0.8), php-tecnickcom-tc-lib-pdf-graph (<< 2.0.0), php-tecnickcom-tc-lib-pdf-graph (>= 2.0.12), ${misc:Depends} +Depends: php (>= 8.0.0), php-date, php-tecnickcom-tc-lib-barcode (<< 3.0.0), php-tecnickcom-tc-lib-barcode (>= 2.2.2), php-tecnickcom-tc-lib-color (<< 3.0.0), php-tecnickcom-tc-lib-color (>= 2.0.9), php-tecnickcom-tc-lib-pdf-image (<< 3.0.0), php-tecnickcom-tc-lib-pdf-image (>= 2.0.15), php-tecnickcom-tc-lib-pdf-font (<< 3.0.0), php-tecnickcom-tc-lib-pdf-font (>= 2.3.1), php-tecnickcom-tc-lib-file (<< 3.0.0), php-tecnickcom-tc-lib-file (>= 2.0.12), php-tecnickcom-tc-lib-pdf-encrypt (<< 3.0.0), php-tecnickcom-tc-lib-pdf-encrypt (>= 2.1.3), php-tecnickcom-tc-lib-unicode-data (<< 3.0.0), php-tecnickcom-tc-lib-unicode-data (>= 2.0.12), php-tecnickcom-tc-lib-unicode (<< 3.0.0), php-tecnickcom-tc-lib-unicode (>= 2.0.12), php-tecnickcom-tc-lib-pdf-page (<< 5.0.0), php-tecnickcom-tc-lib-pdf-page (>= 4.0.10), php-tecnickcom-tc-lib-pdf-graph (<< 3.0.0), php-tecnickcom-tc-lib-pdf-graph (>= 2.0.13), ${misc:Depends} Description: PHP Barcode library This library includes PHP classes to generate PDF documents. diff --git a/resources/rpm/rpm.spec b/resources/rpm/rpm.spec index c3630d5..8f679a6 100644 --- a/resources/rpm/rpm.spec +++ b/resources/rpm/rpm.spec @@ -19,26 +19,26 @@ BuildArch: noarch Requires: php(language) >= 8.0.0 Requires: php-date Requires: php-pcre -Requires: php-composer(%{c_vendor}/tc-lib-barcode) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-barcode) >= 2.2.1 -Requires: php-composer(%{c_vendor}/tc-lib-color) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-color) >= 2.0.8 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-image) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-image) >= 2.0.12 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-font) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-font) >= 2.2.3 -Requires: php-composer(%{c_vendor}/tc-lib-file) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-file) >= 2.0.11 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-encrypt) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-encrypt) >= 2.1.2 -Requires: php-composer(%{c_vendor}/tc-lib-unicode-data) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-unicode-data) >= 2.0.11 -Requires: php-composer(%{c_vendor}/tc-lib-unicode) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-unicode) >= 2.0.11 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-page) < 3.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-page) >= 4.0.8 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-graph) < 2.0.0 -Requires: php-composer(%{c_vendor}/tc-lib-pdf-graph) >= 2.0.12 +Requires: php-composer(%{c_vendor}/tc-lib-barcode) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-barcode) >= 2.2.2 +Requires: php-composer(%{c_vendor}/tc-lib-color) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-color) >= 2.0.9 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-image) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-image) >= 2.0.15 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-font) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-font) >= 2.3.1 +Requires: php-composer(%{c_vendor}/tc-lib-file) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-file) >= 2.0.12 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-encrypt) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-encrypt) >= 2.1.3 +Requires: php-composer(%{c_vendor}/tc-lib-unicode-data) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-unicode-data) >= 2.0.12 +Requires: php-composer(%{c_vendor}/tc-lib-unicode) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-unicode) >= 2.0.12 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-page) < 5.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-page) >= 4.0.10 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-graph) < 3.0.0 +Requires: php-composer(%{c_vendor}/tc-lib-pdf-graph) >= 2.0.13 Provides: php-composer(%{c_vendor}/%{gh_project}) = %{version} Provides: php-%{gh_project} = %{version} diff --git a/src/Base.php b/src/Base.php index 263e97e..253651b 100644 --- a/src/Base.php +++ b/src/Base.php @@ -16,16 +16,16 @@ namespace Com\Tecnick\Pdf; -use Com\Tecnick\Barcode\Barcode; -use Com\Tecnick\Color\Pdf; -use Com\Tecnick\File\Cache; -use Com\Tecnick\File\File; -use Com\Tecnick\Pdf\Encrypt\Encrypt; -use Com\Tecnick\Pdf\Font\Stack; -use Com\Tecnick\Pdf\Graph\Draw; -use Com\Tecnick\Pdf\Image\Import; -use Com\Tecnick\Pdf\Page\Page; -use Com\Tecnick\Unicode\Convert; +use Com\Tecnick\Barcode\Barcode as ObjBarcode; +use Com\Tecnick\Color\Pdf as ObjColor; +use Com\Tecnick\File\Cache as ObjCache; +use Com\Tecnick\File\File as ObjFile; +use Com\Tecnick\Pdf\Encrypt\Encrypt as ObjEncrypt; +use Com\Tecnick\Pdf\Font\Stack as ObjFont; +use Com\Tecnick\Pdf\Graph\Draw as ObjGraph; +use Com\Tecnick\Pdf\Image\Import as ObjImage; +use Com\Tecnick\Pdf\Page\Page as ObjPage; +use Com\Tecnick\Unicode\Convert as ObjUniConvert; /** * Com\Tecnick\Pdf\Base @@ -60,73 +60,83 @@ * 'NumCopies'?: int, * } * + * @phpstan-type TBBox array{ + * 'x': float, + * 'y': float, + * 'w': float, + * 'h': float, + * } + * + * @phpstan-type TStackBBox array + * + * @phpstan-import-type TAnnot from Output * @phpstan-import-type TEmbeddedFile from Output + * @phpstan-import-type TObjID from Output * @phpstan-import-type TOutline from Output - * @phpstan-import-type TAnnot from Output - * @phpstan-import-type TXOBject from Output * @phpstan-import-type TSignature from Output * @phpstan-import-type TSignTimeStamp from Output + * @phpstan-import-type TGTransparency from Output * @phpstan-import-type TUserRights from Output - * @phpstan-import-type TObjID from Output + * @phpstan-import-type TXOBject from Output * * @SuppressWarnings(PHPMD) */ abstract class Base { - /** - * Encrypt object - */ - public Encrypt $encrypt; - - /** - * Color object - */ - public Pdf $color; - - /** - * Barcode object - */ - public Barcode $barcode; - - /** - * File object - */ - public File $file; - - /** - * Cache object - */ - public Cache $cache; - - /** - * Unicode Convert object - */ - public Convert $uniconv; - - /** - * Page object - */ - public Page $page; - - /** - * Graph object - */ - public Draw $graph; - - /** - * Font object - */ - public Stack $font; - - /** - * Image Import object - */ - public Import $image; + /** + * Encrypt object. + */ + public ObjEncrypt $encrypt; + + /** + * Color object. + */ + public ObjColor $color; + + /** + * Barcode object. + */ + public ObjBarcode $barcode; + + /** + * File object. + */ + public ObjFile $file; + + /** + * Cache object. + */ + public ObjCache $cache; + + /** + * Unicode Convert object. + */ + public ObjUniConvert $uniconv; + + /** + * Page object. + */ + public ObjPage $page; + + /** + * Graph object. + */ + public ObjGraph $graph; + + /** + * Font object. + */ + public ObjFont $font; + + /** + * Image Import object. + */ + public ObjImage $image; /** * TCPDF version. */ - protected string $version = '8.0.66'; + protected string $version = '8.0.68'; /** * Time is seconds since EPOCH when the document was created. @@ -341,6 +351,13 @@ abstract class Base */ protected array $xobject = []; + /** + * Current XOBject template ID. + * + * @var string + */ + protected string $xobjtid = ''; + /** * Outlines Data. * @@ -438,6 +455,18 @@ abstract class Base */ protected array $xobjects = []; + /** + * Stack of bounding boxes [x, y, width, height] in user units. + * + * @var TStackBBox + */ + protected $bbox = [[ + 'x' => 0, + 'y' => 0, + 'w' => 0, + 'h' => 0, + ]]; + /** * Convert user units to internal points unit. * diff --git a/src/ClassObjects.php b/src/ClassObjects.php index 3f2bd2e..6ad26c9 100644 --- a/src/ClassObjects.php +++ b/src/ClassObjects.php @@ -39,20 +39,29 @@ * @copyright 2002-2024 Nicola Asuni - Tecnick.com LTD * @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT) * @link https://github.com/tecnickcom/tc-lib-pdf + * */ abstract class ClassObjects extends \Com\Tecnick\Pdf\Output { - /** - * Initialize class objects - */ - protected function initClassObjects(): void - { + /** + * Initialize dependencies class objects. + * + * @param ?ObjEncrypt $objEncrypt Encryption object. + */ + public function initClassObjects( + ?ObjEncrypt $objEncrypt = null + ): void { + if ($objEncrypt instanceof ObjEncrypt) { + $this->encrypt = $objEncrypt; + } else { + $this->encrypt = new ObjEncrypt(); + } + $this->color = new ObjColor(); $this->barcode = new ObjBarcode(); $this->file = new ObjFile(); $this->cache = new ObjCache(); $this->uniconv = new ObjUniConvert(); - $this->encrypt = new ObjEncrypt(); $this->page = new ObjPage( $this->unit, @@ -60,8 +69,9 @@ protected function initClassObjects(): void $this->encrypt, (bool) $this->pdfa, $this->compress, - $this->sigapp + $this->sigapp, ); + $this->kunit = $this->page->getKUnit(); $this->graph = new ObjGraph( @@ -71,33 +81,21 @@ protected function initClassObjects(): void $this->color, $this->encrypt, (bool) $this->pdfa, - $this->compress + $this->compress, ); $this->font = new ObjFont( $this->kunit, $this->subsetfont, $this->isunicode, - (bool) $this->pdfa + (bool) $this->pdfa, ); $this->image = new ObjImage( $this->kunit, $this->encrypt, (bool) $this->pdfa, - $this->compress + $this->compress, ); } - - /** - * Enable or disable the the Signature Approval - * - * @param bool $enabled It true enable the Signature Approval - */ - protected function enableSignatureApproval(bool $enabled = true): static - { - $this->sigapp = $enabled; - $this->page->enableSignatureApproval($this->sigapp); - return $this; - } } diff --git a/src/Output.php b/src/Output.php index 48faf44..41897b6 100644 --- a/src/Output.php +++ b/src/Output.php @@ -18,6 +18,10 @@ use Com\Tecnick\Pdf\Exception as PdfException; use Com\Tecnick\Pdf\Font\Output as OutFont; +use Com\Tecnick\Color\Pdf as ObjColor; +use Com\Tecnick\Pdf\Font\Stack as ObjFont; +use Com\Tecnick\Pdf\Graph\Draw as ObjGraph; +use Com\Tecnick\Pdf\Image\Import as ObjImage; /** * Com\Tecnick\Pdf\Output @@ -330,24 +334,29 @@ * 'opt': TAnnotOpts, * } * + * @phpstan-type TGTransparency array{ + * 'CS': string, + * 'I': bool, + * 'K': bool, + * } + * * @phpstan-type TXOBject array{ - * 'extgstates'?: \Com\Tecnick\Pdf\Graph\Draw, - * 'fonts'?: \Com\Tecnick\Pdf\Font\Stack, - * 'gradients'?: \Com\Tecnick\Pdf\Graph\Draw, - * 'group'?: array{ - * 'CS'?: string, - * 'I'?: bool, - * 'K'?: bool, - * }, - * 'h': float, - * 'images'?: array, + * 'colorspace'?: array, + * 'extgstate'?: array, + * 'font'?: array, + * 'image'?: array, + * 'pattern'?: array, + * 'shading'?: array, + * 'xobject'?: array, + * 'annotations'?: array, + * 'transparency'?: ?TGTransparency, + * 'id': string, + * 'outdata': string, * 'n': int, - * 'outdata'?: string, - * 'spot_colors'?: \Com\Tecnick\Color\Pdf, - * 'w': float, * 'x': float, - * 'xobjects'?: array, * 'y': float, + * 'w': float, + * 'h': float, * } * * @phpstan-type TOutline array{ @@ -548,13 +557,13 @@ protected function getOutPDFBody(): string $out .= $this->graph->getOutExtGState($this->pon); $this->pon = $this->graph->getObjectNumber(); $out .= $this->getOutOCG(); - $output = new OutFont( + $outfont = new OutFont( $this->font->getFonts(), $this->pon, - $this->encrypt + $this->encrypt, ); - $out .= $output->getFontsBlock(); - $this->pon = $output->getObjectNumber(); + $out .= $outfont->getFontsBlock(); + $this->pon = $outfont->getObjectNumber(); $out .= $this->image->getOutImagesBlock($this->pon); $this->pon = $this->image->getObjectNumber(); $out .= $this->color->getPdfSpotObjects($this->pon); @@ -964,12 +973,15 @@ protected function getOutAPXObjects( $stream = trim($stream); $oid = ++$this->pon; $out = $oid . ' 0 obj' . "\n"; - $this->xobjects['AX' . $oid] = [ + $tid = 'AX' . $oid; + $this->xobjects[$tid] = [ + 'id' => $tid, 'n' => $oid, - 'h' => 0, - 'w' => 0, 'x' => 0, + 'w' => 0, 'y' => 0, + 'h' => 0, + 'outdata' => '', ]; $out .= '<< /Type /XObject /Subtype /Form /FormType 1'; if ($this->compress) { @@ -1025,66 +1037,77 @@ protected function getOutXObjects(): string $this->toPoints(($data['w'] + $data['x'])), $this->toPoints(($data['h'] - $data['y'])) ); - $out .= ' /Matrix [1 0 0 1 0 0] /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; - if (! empty($data['fonts'])) { - $fonts = $data['fonts']->getFonts(); - $out = ' /Font <<'; - foreach ($fonts as $font) { - $out .= ' /F' . $font['i'] . ' ' . $font['n'] . ' 0 R'; + $out .= ' /Matrix [1 0 0 1 0 0] /Resources <<' + . ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; + + if (empty($this->pdfa) || ($this->pdfa > 1)) { + if (!empty($data['extgstate'])) { + $out .= ' /ExtGState <<'; + foreach ($data['extgstate'] as $objdic) { + $out .= $objdic; + } + $out .= ' >>'; } - $out .= ' >>'; - } + if (!empty($data['pattern'])) { + $out .= ' /Pattern <<'; + foreach ($data['pattern'] as $objdic) { + $out .= $objdic; + } + $out .= ' >>'; + } - if (! empty($data['extgstates'])) { - $out .= $data['extgstates']->getOutExtGStateResources(); + if (!empty($data['shading'])) { + $out .= ' /Shading <<'; + foreach ($data['shading'] as $objdic) { + $out .= $objdic; + } + $out .= ' >>'; + } } - if (! empty($data['gradients'])) { - $out .= $data['gradients']->getOutGradientResources(); + if (! empty($data['colorspace'])) { + $out .= ' /ColorSpace <<'; + foreach ($data['colorspace'] as $objdic) { + $out .= $objdic; + } + $out .= ' >>'; } - if (! empty($data['spot_colors'])) { - $out .= $data['spot_colors']->getPdfSpotResources(); + if (! empty($data['font'])) { + $out .= ' /Font <<'; + foreach ($data['font'] as $objdic) { + $out .= $objdic; + } + $out .= ' >>'; } - // images or nested xobjects - if (! empty($data['images']) || ! empty($data['xobjects'])) { + if (! empty($data['image']) || ! empty($data['xobject'])) { $out .= ' /XObject <<'; - - if (! empty($data['images'])) { - foreach ($data['images'] as $imgid) { - $out .= ' /I' . $imgid . ' ' . $this->xobject['I' . $imgid]['n'] . ' 0 R'; + if (! empty($data['image'])) { + foreach ($data['image'] as $objdic) { + $out .= $objdic; } } - if (! empty($data['xobjects'])) { - foreach ($data['xobjects'] as $sub_id => $sub_objid) { - $out .= ' /' . $sub_id . ' ' . $sub_objid . ' 0 R'; + if (! empty($data['xobject'])) { + foreach ($data['xobject'] as $objdic) { + $out .= $objdic; } } - $out .= ' >>'; } - $out .= ' >>'; - if (! empty($data['group'])) { + $out .= ' >>'; // end of /Resources. + + if (! empty($data['transparency'])) { // set transparency group $out .= ' /Group << /Type /Group /S /Transparency'; - if (is_array($data['group'])) { - if (! empty($data['group']['CS'])) { - $out .= ' /CS /' . $data['group']['CS']; - } - - if (isset($data['group']['I'])) { - $out .= ' /I /' . ($data['group']['I'] === true ? 'true' : 'false'); - } - - if (isset($data['group']['K'])) { - $out .= ' /K /' . ($data['group']['K'] === true ? 'true' : 'false'); - } + if (!empty($data['transparency'])) { + $out .= ' /CS /' . $data['transparency']['CS']; + $out .= ' /I /' . (($data['transparency']['I'] === true) ? 'true' : 'false'); + $out .= ' /K /' . (($data['transparency']['K'] === true) ? 'true' : 'false'); } - $out .= ' >>'; } @@ -1754,7 +1777,8 @@ protected function getOutAnnotationOptSubtypeLink( if (! empty($annot['txt']) && is_string($annot['txt'])) { switch ($annot['txt'][0]) { case '#': // internal destination - $out .= ' /A << /S /GoTo /D /' . $this->encrypt->encodeNameObject(substr($annot['txt'], 1)) . '>>'; + $out .= ' /A << /S /GoTo /D /' + . $this->encrypt->encodeNameObject(substr($annot['txt'], 1)) . '>>'; break; case '%': // embedded PDF file $filename = basename(substr($annot['txt'], 1)); @@ -1787,7 +1811,8 @@ protected function getOutAnnotationOptSubtypeLink( } $out .= ' /A << /S /GoToR /D ' . $dest - . ' /F ' . $this->encrypt->escapeDataString($this->unhtmlentities($parsedUrl['path']), $oid) + . ' /F ' + . $this->encrypt->escapeDataString($this->unhtmlentities($parsedUrl['path']), $oid) . ' /NewWindow true' . ' >>'; } else { @@ -2835,7 +2860,7 @@ protected function getOutSignature(): string // optional digest data (values must be calculated and replaced later) //$out .= ' /Data ********** 0 R' - // .' /DigestMethod/MD5' + // .' /DigestMethod /MD5' // .' /DigestLocation[********** 34]' // .' /DigestValue<********************************>'; $out .= ' >> ]'; // end of reference diff --git a/src/Tcpdf.php b/src/Tcpdf.php index 9863f7a..abb7c68 100644 --- a/src/Tcpdf.php +++ b/src/Tcpdf.php @@ -19,6 +19,10 @@ use Com\Tecnick\Barcode\Exception as BarcodeException; use Com\Tecnick\Pdf\Encrypt\Encrypt as ObjEncrypt; use Com\Tecnick\Pdf\Exception as PdfException; +use Com\Tecnick\Color\Pdf as ObjColor; +use Com\Tecnick\Pdf\Font\Stack as ObjFont; +use Com\Tecnick\Pdf\Graph\Draw as ObjGraph; +use Com\Tecnick\Pdf\Image\Import as ObjImage; /** * Com\Tecnick\Pdf\Tcpdf @@ -39,7 +43,9 @@ * @phpstan-import-type TAnnotOpts from Output * @phpstan-import-type TSignature from Output * @phpstan-import-type TSignTimeStamp from Output + * @phpstan-import-type TGTransparency from Output * @phpstan-import-type TUserRights from Output + * @phpstan-import-type TXOBject from Output * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ @@ -75,10 +81,7 @@ public function __construct( $this->setPDFMode($mode); $this->setCompressMode($compress); $this->setPDFVersion(); - $this->initClassObjects(); - if ($objEncrypt instanceof \Com\Tecnick\Pdf\Encrypt\Encrypt) { - $this->encrypt = $objEncrypt; - } + $this->initClassObjects($objEncrypt); } /** @@ -291,6 +294,21 @@ public function setAnnotation( 'subtype' => 'text', ] ): int { + if (!empty($this->xobjtid)) { + // Store annotationparameters for later use on a XObject template. + $this->xobject[$this->xobjtid]['annotations'][] = [ + 'n' => 0, + 'x' => $posx, + 'y' => $posy, + 'w' => $width, + 'h' => $height, + 'txt' => $txt, + 'opt' => $opt, + ]; + + return 0; + } + $oid = ++$this->pon; $this->annotation[$oid] = [ 'n' => $oid, @@ -434,6 +452,19 @@ public function setSignature(array $data): void $this->sign = true; } + + /** + * Enable or disable the the Signature Approval + * + * @param bool $enabled It true enable the Signature Approval + */ + protected function enableSignatureApproval(bool $enabled = true): static + { + $this->sigapp = $enabled; + $this->page->enableSignatureApproval($this->sigapp); + return $this; + } + /** * Set the signature timestamp. * @@ -464,18 +495,18 @@ public function setSignTimeStamp(array $data): void * @param string $name Name of the signature. * * @return array{ - * 'name': string, - * 'page': int, - * 'rect': string, - * } Array defining page and rectangle coordinates of signature appearance. + * 'name': string, + * 'page': int, + * 'rect': string, + * } Array defining page and rectangle coordinates of signature appearance. */ protected function getSignatureAppearanceArray( - $posx = 0, - $posy = 0, - $width = 0, - $heigth = 0, - $page = -1, - $name = '' + float $posx = 0, + float $posy = 0, + float $width = 0, + float $heigth = 0, + int $page = -1, + string $name = '' ): array { $sigapp = []; @@ -493,7 +524,7 @@ protected function getSignatureAppearanceArray( } /** - * Set the digital signature appearance (a cliccable rectangle area to get signature properties) + * Set the digital signature appearance (a cliccable rectangle area to get signature properties). * * @param float $posx Abscissa of the upper-left corner. * @param float $posy Ordinate of the upper-left corner. @@ -503,12 +534,12 @@ protected function getSignatureAppearanceArray( * @param string $name Name of the signature. */ public function setSignatureAppearance( - $posx = 0, - $posy = 0, - $width = 0, - $heigth = 0, - $page = -1, - $name = '' + float $posx = 0, + float $posy = 0, + float $width = 0, + float $heigth = 0, + int $page = -1, + string $name = '' ): void { $data = $this->getSignatureAppearanceArray($posx, $posy, $width, $heigth, $page, $name); $this->signature['appearance']['page'] = $data['page']; @@ -518,7 +549,7 @@ public function setSignatureAppearance( } /** - * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) + * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties). * * @param float $posx Abscissa of the upper-left corner. * @param float $posy Ordinate of the upper-left corner. @@ -528,12 +559,12 @@ public function setSignatureAppearance( * @param string $name Name of the signature. */ public function addEmptySignatureAppearance( - $posx = 0, - $posy = 0, - $width = 0, - $heigth = 0, - $page = -1, - $name = '' + float $posx = 0, + float $posy = 0, + float $width = 0, + float $heigth = 0, + int $page = -1, + string $name = '' ): void { ++$this->pon; $data = $this->getSignatureAppearanceArray($posx, $posy, $width, $heigth, $page, $name); @@ -546,6 +577,9 @@ public function addEmptySignatureAppearance( $this->setSignAnnotRefs(); } + /* + * Set the signature annotation references. + */ protected function setSignAnnotRefs(): void { if (empty($this->objid['signature'])) { @@ -564,4 +598,166 @@ protected function setSignAnnotRefs(): void $this->page->addAnnotRef($esa['objid'], $esa['page']); } } + + /** + * Create a new XObject template and return the object id. + * + * An XObject Template is a PDF block that is a self-contained description + * of any sequence of graphics objects (including path objects, text objects, + * and sampled images). An XObject Template may be painted multiple times, + * either on several pages or at several locations on the same page and + * produces the same results each time, subject only to the graphics state + * at the time it is invoked. + * + * @param float $width Width of the XObject. + * @param float $heigth Height of the XObject. + * @param ?TGTransparency $transpgroup Optional group attributes. + * + * @return TXOBject XObject template object. + */ + public function newXObjectTemplate( + float $width = 0, + float $heigth = 0, + ?array $transpgroup = null, + ): array { + $oid = ++$this->pon; + $tid = 'XT' . $oid; + $this->xobjtid = $tid; + + $region = $this->page->getRegion(); + + if (empty($width) || $width < 0) { + $width = $region['RW']; + } + + if (empty($heigth) || $heigth < 0) { + $heigth = $region['RH']; + } + + $this->xobject[$tid] = [ + 'id' => $tid, + 'n' => $oid, + 'x' => 0, + 'y' => 0, + 'w' => $width, + 'h' => $heigth, + 'outdata' => '', + 'transpgroup' => $transpgroup, + ]; + + return $this->xobject[$tid]; + } + + /** + * Exit from the XObject template mode. + * + * See: newXObjectTemplate. + */ + public function exitXObjectTemplate(): void + { + $this->xobjtid = ''; + } + + /** + * Returns the PDF code to render the specified XObject template. + * + * See: newXObjectTemplate. + * + * @param TXOBject $xobj The XObject Template object as returned by the newXObjectTemplate method. + * @param float $posx Abscissa of upper-left corner. + * @param float $posy Ordinate of upper-left corner. + * @param float $width Width. + * @param float $height Height. + * @param string $valign Vertical alignment inside the specified box: T=top; C=center; B=bottom. + * @param string $halign Horizontal alignment inside the specified box: L=left; C=center; R=right. + * + * @return string The PDF code to render the specified XObject template. + */ + public function getXObjectTemplate( + array $xobj, + float $posx = 0, + float $posy = 0, + float $width = 0, + float $height = 0, + string $valign = 'T', + string $halign = 'L', + ): string { + $this->xobjtid = ''; + $region = $this->page->getRegion(); + + if (empty($width) || $width < 0) { + $width = min($xobj['w'], $region['RW']); + } + + if (empty($height) || $height < 0) { + $height = min($xobj['h'], $region['RH']); + } + + $tplx = $this->cellHPos($posx, $width, $halign, $this->defcell); + $tply = $this->cellVPos($posy, $height, $valign, $this->defcell); + + $this->bbox[] = [ + 'x' => $tplx, + 'y' => $tply, + 'w' => $width, + 'h' => $height, + ]; + + $out = $this->graph->getStartTransform(); + $ctm = [ + 0 => ($width / $xobj['w']), + 1 => 0, + 2 => 0, + 3 => ($height / $xobj['h']), + 4 => $this->toPoints($tplx), + 5 => $this->toYPoints($tply + $height), + ]; + $out .= $this->graph->getTransformation($ctm); + $out .= '/' . $xobj['id'] . ' Do' . "\n"; + $out .= $this->graph->getStopTransform(); + + if (!empty($xobj['annotations'])) { + foreach ($xobj['annotations'] as $annot) { + // transform original coordinates + $clt = $this->graph->getCtmProduct( + $ctm, + array( + 1, + 0, + 0, + 1, + $this->toPoints($annot['x']), + $this->toPoints(-$annot['y']), + ), + ); + $anx = $this->toUnit($clt[4]); + $any = $this->toYUnit($clt[5] + $this->toUnit($height)); + + $crb = $this->graph->getCtmProduct( + $ctm, + array( + 1, + 0, + 0, + 1, + $this->toPoints(($annot['x'] + $annot['w'])), + $this->toPoints((-$annot['y'] - $annot['h'])), + ), + ); + $anw = $this->toUnit($crb[4]) - $anx; + $anh = $this->toYUnit($crb[5] + $this->toUnit($height)) - $any; + + $out .= $this->setAnnotation( + $anx, + $any, + $anw, + $anh, + $annot['txt'], + $annot['opt'] + ); + } + } + + return $out; + } } diff --git a/src/Text.php b/src/Text.php index 539d8c2..5378067 100644 --- a/src/Text.php +++ b/src/Text.php @@ -37,12 +37,9 @@ * @phpstan-import-type StyleDataOpt from \Com\Tecnick\Pdf\Cell * @phpstan-import-type TCellDef from \Com\Tecnick\Pdf\Cell * - * @phpstan-type TextBBox array{ - * 'x': float, - * 'y': float, - * 'width': float, - * 'height': float, - * } + * + * @phpstan-import-type TBBox from \Com\Tecnick\Pdf\Base + * @phpstan-import-type TStackBBox from \Com\Tecnick\Pdf\Base * * @phpstan-type TextShadow array{ * 'xoffset': float, @@ -107,18 +104,6 @@ abstract class Text extends \Com\Tecnick\Pdf\Cell */ protected $autozerowidthbreaks = false; - /** - * Last text bounding box [x, y, width, height] in user units. - * - * @var TextBBox - */ - protected $lasttxtbbox = [ - 'x' => 0, - 'y' => 0, - 'width' => 0, - 'height' => 0, - ]; - /** * Returns the PDF code to render a text block inside a rectangular cell. * @@ -580,7 +565,8 @@ protected function outTextLines( $offset = 0; $line_posx = $posx; - $line_posy = ($this->lasttxtbbox['y'] + $this->lasttxtbbox['height'] + $fontascent + $linespace); + $bbox = $this->getLastBBox(); + $line_posy = ($bbox['y'] + $bbox['h'] + $fontascent + $linespace); } return $out; @@ -918,11 +904,11 @@ protected function outTextLine( $width = $width > 0 ? $width : 0; $curfont = $this->font->getCurrentFont(); - $this->lasttxtbbox = [ + $this->bbox[] = [ 'x' => $posx, 'y' => ($posy - $this->toUnit($curfont['ascent'])), - 'width' => $width, - 'height' => $this->toUnit($curfont['height']), + 'w' => $width, + 'h' => $this->toUnit($curfont['height']), ]; $out = $this->getJustifiedString($txt, $ordarr, $dim, $width); $out = $this->getOutTextPosXY($out, $posx, $posy, 'Td'); @@ -941,11 +927,11 @@ protected function outTextLine( /** * Returns the last text bounding box [llx, lly, urx, ury]. * - * @return TextBBox Array of bounding box values. + * @return TBBox Array of bounding box values. */ - public function getLastTextBBox(): array + public function getLastBBox(): array { - return $this->lasttxtbbox; + return $this->bbox[array_key_last($this->bbox)]; } /** @@ -980,6 +966,9 @@ protected function getJustifiedString( ): string { $pwidth = $this->toPoints($width); + $this->bbox[] = $this->getLastBBox(); + $bboxid = array_key_last($this->bbox); + if ((!$this->isunicode) || $this->font->isCurrentByteFont()) { if ($this->isunicode) { $txt = $this->uniconv->latinArrToStr($this->uniconv->uniArrToLatinArr($ordarr)); @@ -990,7 +979,7 @@ protected function getJustifiedString( $spacewidth = (($pwidth - $dim['totwidth']) / ($dim['spaces'] ?: 1)); return $this->getOutTextStateOperatorTw($txt, $spacewidth); } - $this->lasttxtbbox['width'] = $this->toUnit($dim['totwidth']); + $this->bbox[$bboxid]['w'] = $this->toUnit($dim['totwidth']); return $txt; } @@ -999,7 +988,7 @@ protected function getJustifiedString( $txt = $this->encrypt->escapeString($txt); if ($pwidth <= 0) { - $this->lasttxtbbox['width'] = $this->toUnit($dim['totwidth']); + $this->bbox[$bboxid]['w'] = $this->toUnit($dim['totwidth']); return $this->getOutTextShowing($txt, 'Tj'); }