diff --git a/composer.json b/composer.json index 69043a3547..e79d68b1d7 100644 --- a/composer.json +++ b/composer.json @@ -5,13 +5,12 @@ "license": "AGPL", "require": { "endroid/qr-code": "^4.6", - "iio/libmergepdf": "dev-move-tcpdi-parser-to-package", "jsignpdf/jsignpdf-php": "^1.2", "mikehaertl/php-pdftk": "^0.13.0", "pagerfanta/pagerfanta": "^3.6", "smalot/pdfparser": "^2.4", "symfony/console": "^5.4", - "tecnickcom/tcpdf": "^6.4", + "tecnickcom/tcpdf": "^6.7", "wobeto/email-blur": "^1.0" }, "require-dev": { @@ -62,11 +61,5 @@ "psr-4": { "OCP\\": "vendor/nextcloud/ocp/OCP" } - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/LibreCodeCoop/libmergepdf" - } - ] + } } diff --git a/composer.lock b/composer.lock index d790805d62..569c744780 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "72977a0e93b9c15c9746c12691a768ee", + "content-hash": "3b8c5d2bdc7f7847621f5e16ffc8e7fc", "packages": [ { "name": "bacon/bacon-qr-code", @@ -185,65 +185,6 @@ ], "time": "2023-03-30T18:46:02+00:00" }, - { - "name": "iio/libmergepdf", - "version": "dev-move-tcpdi-parser-to-package", - "source": { - "type": "git", - "url": "https://github.com/LibreSign/libmergepdf.git", - "reference": "4304a115056e595725e6105a42e47cebe0416ed2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/LibreSign/libmergepdf/zipball/4304a115056e595725e6105a42e47cebe0416ed2", - "reference": "4304a115056e595725e6105a42e47cebe0416ed2", - "shasum": "" - }, - "require": { - "libresign/tcpdi_parser": "^0.4.0", - "php": "^7.1||^8.0", - "setasign/fpdi": "^2", - "tecnickcom/tcpdf": "^6.2.22" - }, - "conflict": { - "rafikhaceb/tcpdi": "*", - "setasign/fpdf": "*" - }, - "require-dev": { - "behat/behat": "^3.10", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "smalot/pdfparser": "~0.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "iio\\libmergepdf\\": "src/" - }, - "classmap": [ - "tcpdi/" - ] - }, - "license": [ - "WTFPL" - ], - "authors": [ - { - "name": "Hannes Forsgård", - "email": "hannes.forsgard@fripost.org" - } - ], - "description": "Library for merging multiple PDFs", - "homepage": "https://github.com/hanneskod/libmergepdf", - "keywords": [ - "merge", - "pdf" - ], - "support": { - "source": "https://github.com/LibreSign/libmergepdf/tree/move-tcpdi-parser-to-package" - }, - "time": "2022-01-30T18:52:29+00:00" - }, { "name": "jsignpdf/jsignpdf-php", "version": "v1.2.3", @@ -297,45 +238,6 @@ }, "time": "2022-12-02T17:27:21+00:00" }, - { - "name": "libresign/tcpdi_parser", - "version": "v0.4", - "source": { - "type": "git", - "url": "https://github.com/LibreSign/tcpdi_parser.git", - "reference": "c958caddb089e8986a509dc538cda0c5e14f18a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/LibreSign/tcpdi_parser/zipball/c958caddb089e8986a509dc538cda0c5e14f18a2", - "reference": "c958caddb089e8986a509dc538cda0c5e14f18a2", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "LibreSign\\TcpdiParser\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Vitor Mattos", - "email": "vitor@php.rio" - }, - { - "name": "Paul Nicholls", - "homepage": "https://github.com/pauln" - } - ], - "support": { - "source": "https://github.com/LibreSign/tcpdi_parser/tree/v0.4" - }, - "time": "2022-01-30T18:39:05+00:00" - }, { "name": "mikehaertl/php-pdftk", "version": "0.13.1", @@ -631,78 +533,6 @@ }, "time": "2021-11-05T16:47:00+00:00" }, - { - "name": "setasign/fpdi", - "version": "v2.6.0", - "source": { - "type": "git", - "url": "https://github.com/Setasign/FPDI.git", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/a6db878129ec6c7e141316ee71872923e7f1b7ad", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad", - "shasum": "" - }, - "require": { - "ext-zlib": "*", - "php": "^5.6 || ^7.0 || ^8.0" - }, - "conflict": { - "setasign/tfpdf": "<1.31" - }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "setasign/fpdf": "~1.8.6", - "setasign/tfpdf": "~1.33", - "squizlabs/php_codesniffer": "^3.5", - "tecnickcom/tcpdf": "~6.2" - }, - "suggest": { - "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." - }, - "type": "library", - "autoload": { - "psr-4": { - "setasign\\Fpdi\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Slabon", - "email": "jan.slabon@setasign.com", - "homepage": "https://www.setasign.com" - }, - { - "name": "Maximilian Kresse", - "email": "maximilian.kresse@setasign.com", - "homepage": "https://www.setasign.com" - } - ], - "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", - "homepage": "https://www.setasign.com/fpdi", - "keywords": [ - "fpdf", - "fpdi", - "pdf" - ], - "support": { - "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", - "type": "tidelift" - } - ], - "time": "2023-12-11T16:03:32+00:00" - }, { "name": "smalot/pdfparser", "version": "v2.9.0", @@ -1567,12 +1397,12 @@ "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "f9fd21807cbb5d43ed62c685e2d6467515d31746" + "reference": "d4adef47ca21c90e6483d59dcb9e5b1023696937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/f9fd21807cbb5d43ed62c685e2d6467515d31746", - "reference": "f9fd21807cbb5d43ed62c685e2d6467515d31746", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/d4adef47ca21c90e6483d59dcb9e5b1023696937", + "reference": "d4adef47ca21c90e6483d59dcb9e5b1023696937", "shasum": "" }, "require": { @@ -1631,7 +1461,7 @@ "type": "custom" } ], - "time": "2024-03-21T09:36:59+00:00" + "time": "2024-03-25T23:56:24+00:00" }, { "name": "wobeto/email-blur", @@ -2668,7 +2498,6 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "iio/libmergepdf": 20, "nextcloud/ocp": 20, "roave/security-advisories": 20 }, diff --git a/lib/Db/File.php b/lib/Db/File.php index 2adf76b4db..dc44812ff1 100644 --- a/lib/Db/File.php +++ b/lib/Db/File.php @@ -25,6 +25,7 @@ namespace OCA\Libresign\Db; use OCP\AppFramework\Db\Entity; +use stdClass; /** * @method void setId(int $id) @@ -45,8 +46,7 @@ * @method string getCallback() * @method void setStatus(int $status) * @method int getStatus() - * @method void setPages(int $pages) - * @method int getPages() + * @method string getMetadata() */ class File extends Entity { /** @var integer */ @@ -97,4 +97,16 @@ public function __construct() { $this->addType('status', 'integer'); $this->addType('metadata', 'string'); } + + public function setMetadata($metadata): void { + if (is_array($metadata)) { + $metadata = json_encode($metadata); + } + $this->metadata = (string) $metadata; + $this->markFieldUpdated('metadata'); + } + + public function getMetadataDecoded(): ?stdClass { + return json_decode($this->metadata); + } } diff --git a/lib/Db/SignRequest.php b/lib/Db/SignRequest.php index b722d1fb34..640dae2fc1 100644 --- a/lib/Db/SignRequest.php +++ b/lib/Db/SignRequest.php @@ -43,7 +43,7 @@ * @method void setDisplayName(string $displayName) * @method string getDisplayName() * @method void setMetadata(array $metadata) - * @method string getMetadata() + * @method array getMetadata() */ class SignRequest extends Entity { /** @var integer */ diff --git a/lib/Db/SignRequestMapper.php b/lib/Db/SignRequestMapper.php index ee5c0ad434..04527a4ded 100644 --- a/lib/Db/SignRequestMapper.php +++ b/lib/Db/SignRequestMapper.php @@ -69,9 +69,6 @@ public function incrementNotificationCounter(SignRequest $signRequest, string $m try { $fromDatabase = $this->getById($signRequest->getId()); $metadata = $fromDatabase->getMetadata(); - if (!empty($metadata)) { - $metadata = json_decode($metadata, true); - } if (!isset($metadata['notify'])) { $this->firstNotification = true; } diff --git a/lib/Handler/Pkcs12Handler.php b/lib/Handler/Pkcs12Handler.php index 755d3f60d5..099f07867f 100644 --- a/lib/Handler/Pkcs12Handler.php +++ b/lib/Handler/Pkcs12Handler.php @@ -33,14 +33,16 @@ use Endroid\QrCode\QrCode; use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; use OC\SystemConfig; +use OCA\Libresign\Db\File as FileEntity; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Handler\CertificateEngine\Handler as CertificateEngineHandler; use OCA\Libresign\Service\FolderService; +use OCA\Libresign\Service\PdfParserService; use OCP\AppFramework\Services\IAppConfig; use OCP\Files\File; use OCP\IL10N; use OCP\IURLGenerator; -use TCPDI; +use TCPDF; use TypeError; class Pkcs12Handler extends SignEngineHandler { @@ -59,6 +61,7 @@ public function __construct( private CertificateEngineHandler $certificateEngineHandler, private IL10N $l10n, private JSignPdfHandler $jSignPdfHandler, + private PdfParserService $pdfParserService, ) { } @@ -159,42 +162,33 @@ public function sign(): File { /** * @psalm-suppress MixedReturnStatement */ - public function getFooter(File $file, string $uuid): string { + public function getFooter(File $file, FileEntity $fileEntity): string { $add_footer = (bool) $this->appConfig->getAppValue('add_footer', '1'); if (!$add_footer) { return ''; } + $metadata = $fileEntity->getMetadata(); + if (!is_array($metadata) || !isset($metadata['d'])) { + $metadata = $this->pdfParserService->getMetadata($file); + } $validation_site = $this->appConfig->getAppValue('validation_site'); if ($validation_site) { - $validation_site = rtrim($validation_site, '/').'/'.$uuid; + $validation_site = rtrim($validation_site, '/').'/'.$fileEntity->getUuid(); } else { - $validation_site = $this->urlGenerator->linkToRouteAbsolute('libresign.page.validationFileWithShortUrl', ['uuid' => $uuid]); + $validation_site = $this->urlGenerator->linkToRouteAbsolute('libresign.page.validationFileWithShortUrl', ['uuid' => $fileEntity->getUuid()]); } - $pdf = new TCPDILibresign(); - $pageCount = $pdf->setSourceData($file->getContent()); - - $dimensions = null; - for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { - $pdf->importPage($pageNo); - - // Define dimensions of page - $tpl = $pdf->tpls[$pageNo]; - $dimensions['or'] = $tpl['w'] > $tpl['h'] ? 'L' : 'P'; - $pdf->setPageOrientation($dimensions['or']); - $dimensions = $pdf->getPageDimensions($pageNo - 1); - $dimensions['w'] = $tpl['w']; - $dimensions['h'] = $tpl['h']; - $dimensions['wk'] = $tpl['h']; - $dimensions['hk'] = $tpl['h']; - foreach (['MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'] as $box) { - if (!isset($dimensions[$box])) { - continue; - } - $dimensions[$box]['urx'] = $tpl['h']; - $dimensions[$box]['ury'] = $tpl['w']; - } - $pdf->AddPage($dimensions['or'], $dimensions); + $pdf = new TCPDFLibresign(); + foreach ($metadata['d'] as $dimension) { + $orientation = $dimension['w'] > $dimension['h'] ? 'L' : 'P'; + $pdf->AddPage($orientation, [ + 'MediaBox' => [ + 'llx' => 0, + 'lly' => 0, + 'urx' => $dimension['w'], + 'ury' => $dimension['h'], + ] + ]); $pdf->SetFont('Helvetica'); $pdf->SetFontSize(8); @@ -232,7 +226,7 @@ public function getFooter(File $file, string $uuid): string { return $pdf->Output('', 'S'); } - private function writeQrCode(string $text, TCPDI $fpdf): void { + private function writeQrCode(string $text, TCPDF $fpdf): void { $this->qrCode = QrCode::create($text) ->setEncoding(new Encoding('UTF-8')) ->setErrorCorrectionLevel(new ErrorCorrectionLevelLow()) diff --git a/lib/Handler/TCPDILibresign.php b/lib/Handler/TCPDFLibresign.php similarity index 67% rename from lib/Handler/TCPDILibresign.php rename to lib/Handler/TCPDFLibresign.php index 42c7aac564..ff3409bb2d 100644 --- a/lib/Handler/TCPDILibresign.php +++ b/lib/Handler/TCPDFLibresign.php @@ -24,9 +24,9 @@ namespace OCA\Libresign\Handler; -use TCPDI; +use TCPDF; -class TCPDILibresign extends TCPDI { +class TCPDFLibresign extends TCPDF { /** * @var bool */ @@ -46,33 +46,4 @@ protected function _textstring($s, $n = 0) { } return parent::_textstring($s, $n); } - - /** - * @psalm-return array{w?: mixed, h?: mixed} - */ - public function getPageTplDimension(int $pageNum): array { - if (!$this->tpls) { - return []; - } - return [ - 'w' => $this->tpls[$pageNum]['w'], - 'h' => $this->tpls[$pageNum]['h'] - ]; - } - - public function getPagesMetadata(): array { - $pageCount = current($this->parsers)->getPageCount(); - $data = [ - 'p' => $pageCount - ]; - for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { - $dimensions = $this->getPageTplDimension($pageNo); - if (empty($dimensions['w'])) { - $this->importPage($pageNo); - $dimensions = $this->getPageTplDimension($pageNo); - } - $data['d'][] = $dimensions; - } - return $data; - } } diff --git a/lib/Service/FileElementService.php b/lib/Service/FileElementService.php index 4fa7c8dac6..04122c0ece 100644 --- a/lib/Service/FileElementService.php +++ b/lib/Service/FileElementService.php @@ -34,7 +34,7 @@ class FileElementService { public function __construct( private FileMapper $fileMapper, private FileElementMapper $fileElementMapper, - private ITimeFactory $timeFactory + private ITimeFactory $timeFactory, ) { } @@ -77,13 +77,13 @@ private function getVisibleElementFromProperties(array $properties, string $uuid private function translateCoordinatesToInternalNotation(array $properties, File $file): array { $translated['page'] = $properties['coordinates']['page'] ?? 1; - $metadata = json_decode($file->getMetadata(), true); - $dimension = $metadata['d'][$translated['page'] - 1]; + $metadata = $file->getMetadataDecoded(); + $dimension = $metadata->d[$translated['page'] - 1]; if (isset($properties['coordinates']['ury'])) { $translated['ury'] = $properties['coordinates']['ury']; } elseif (isset($properties['coordinates']['top'])) { - $translated['ury'] = $dimension['h'] - $properties['coordinates']['top']; + $translated['ury'] = $dimension->h - $properties['coordinates']['top']; } else { $translated['ury'] = 0; } @@ -131,12 +131,12 @@ private function translateCoordinatesToInternalNotation(array $properties, File } public function translateCoordinatesFromInternalNotation(array $properties, File $file): array { - $metadata = json_decode($file->getMetadata(), true); - $dimension = $metadata['d'][$properties['coordinates']['page'] - 1]; + $metadata = $file->getMetadataDecoded(); + $dimension = $metadata->d[$properties['coordinates']['page'] - 1]; $translated['left'] = $properties['coordinates']['llx']; $translated['height'] = abs($properties['coordinates']['ury'] - $properties['coordinates']['lly']); - $translated['top'] = $dimension['h'] - $properties['coordinates']['ury']; + $translated['top'] = $dimension->h - $properties['coordinates']['ury']; $translated['width'] = $properties['coordinates']['urx'] - $properties['coordinates']['llx']; return $translated; diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 6bccefccbc..a902bb3d28 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -260,7 +260,7 @@ private function getSigners(): array { private function getPages(): array { $return = []; - $metadata = json_decode($this->file->getMetadata()); + $metadata = $this->file->getMetadataDecoded(); for ($page = 1; $page <= $metadata->p; $page++) { $return[] = [ 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.File.getPage', [ diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index 41789ec21a..ed3da657cc 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -535,7 +535,7 @@ private function getPdfToSign(FileEntity $fileData, File $originalFile): File { $originalFile->getPath() ); - $footer = $this->pkcs12Handler->getFooter($originalFile, $fileData->getUuid()); + $footer = $this->pkcs12Handler->getFooter($originalFile, $fileData); if ($footer) { $background = $this->tempManager->getTemporaryFile('signed.pdf'); file_put_contents($background, $footer); diff --git a/tests/Unit/Handler/Pkcs12HandlerTest.php b/tests/Unit/Handler/Pkcs12HandlerTest.php index 60b50e56e4..561e79a91d 100644 --- a/tests/Unit/Handler/Pkcs12HandlerTest.php +++ b/tests/Unit/Handler/Pkcs12HandlerTest.php @@ -3,10 +3,10 @@ use OC\SystemConfig; use OCA\Libresign\Handler\CertificateEngine\CfsslHandler; use OCA\Libresign\Handler\CertificateEngine\Handler as CertificateEngineHandler; -use OCA\Libresign\Handler\CertificateEngine\OpenSslHandler; use OCA\Libresign\Handler\JSignPdfHandler; use OCA\Libresign\Handler\Pkcs12Handler; use OCA\Libresign\Service\FolderService; +use OCA\Libresign\Service\PdfParserService; use OCP\AppFramework\Services\IAppConfig; use OCP\IL10N; use OCP\IURLGenerator; @@ -21,7 +21,7 @@ final class Pkcs12HandlerTest extends \OCA\Libresign\Tests\Unit\TestCase { private CfsslHandler|MockObject $cfsslHandler; private IL10N|MockObject $l10n; private JSignPdfHandler|MockObject $jSignPdfHandler; - private OpenSslHandler|MockObject $openSslHandler; + private PdfParserService|MockObject $pdfParserService; private CertificateEngineHandler|MockObject $certificateEngineHandler; private array $cfsslHandlerBuffer = []; @@ -36,6 +36,7 @@ public function setUp(): void { ->method('t') ->will($this->returnArgument(0)); $this->jSignPdfHandler = $this->createMock(JSignPdfHandler::class); + $this->pdfParserService = $this->createMock(PdfParserService::class); $this->pkcs12Handler = new Pkcs12Handler( $this->folderService, $this->appConfig, @@ -44,6 +45,7 @@ public function setUp(): void { $this->certificateEngineHandler, $this->l10n, $this->jSignPdfHandler, + $this->pdfParserService, ); } @@ -102,9 +104,11 @@ public function testGetFooterWithoutValidationSite() { $this->certificateEngineHandler, $this->l10n, $this->jSignPdfHandler, + $this->pdfParserService, ); $file = $this->createMock(\OCP\Files\File::class); - $actual = $this->pkcs12Handler->getFooter($file, 'uuid'); + $libresignFile = $this->createMock(\OCA\Libresign\Db\File::class); + $actual = $this->pkcs12Handler->getFooter($file, $libresignFile); $this->assertEmpty($actual); } @@ -130,6 +134,7 @@ public function testGetFooterWithSuccess() { $this->certificateEngineHandler, $this->l10n, $this->jSignPdfHandler, + $this->pdfParserService, ); $file = $this->createMock(\OCP\Files\File::class); @@ -137,8 +142,24 @@ public function testGetFooterWithSuccess() { ->willReturn('small_valid.pdf'); $file->method('getContent') ->willReturn(file_get_contents(__DIR__ . '/../../fixtures/small_valid.pdf')); - $actual = $this->pkcs12Handler->getFooter($file, 'uuid'); - $this->assertEquals(18615, strlen($actual)); + $libresignFile = $this->createMock(\OCA\Libresign\Db\File::class); + $libresignFile + ->method('__call') + ->willReturnCallback(function ($key, $default) { + switch ($key) { + case 'getMetadata': return [ + 'd' => [ + [ + 'w' => 100, + 'h' => 100, + ], + ], + ]; + case 'getUuid': return 'uuid'; + } + }); + $actual = $this->pkcs12Handler->getFooter($file, $libresignFile); + $this->assertEquals(7655, strlen($actual)); } public function cfsslHandlerCallbackToGetSetArguments($functionName, $value = null) {