From 7df8a4906dcf9ce6c9cd05b0f2d0326cb3de0cef Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Tue, 11 Nov 2025 17:18:00 +0100 Subject: [PATCH 1/4] chore(deps): update dev dependencies --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 2075d86..c617bb4 100644 --- a/composer.json +++ b/composer.json @@ -42,10 +42,10 @@ "psr/http-message": "^1.0|^2.0" }, "require-dev": { - "doctrine/coding-standard": "^12.0", - "pestphp/pest": "^2.28", + "doctrine/coding-standard": "^14.0", + "pestphp/pest": "^2.36", "phpstan/phpstan": "^1.12", - "squizlabs/php_codesniffer": "^3.10" + "squizlabs/php_codesniffer": "^4.0" }, "autoload": { "psr-4": { From c325cc73daa241863e1b82c682af12549b93636c Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Tue, 11 Nov 2025 17:34:08 +0100 Subject: [PATCH 2/4] feat: add the encrypt feature and add missing flatten method in the Chromium module --- src/Modules/ChromiumPdf.php | 21 ++++++++++++ src/Modules/LibreOffice.php | 11 +++++++ src/Modules/PdfEngines.php | 30 +++++++++++++++++ tests/Modules/ChromiumPdfTest.php | 54 +++++++++++++++++++++++++++++++ tests/Modules/LibreOfficeTest.php | 10 ++++++ tests/Modules/PdfEnginesTest.php | 49 ++++++++++++++++++++++++++-- 6 files changed, 173 insertions(+), 2 deletions(-) diff --git a/src/Modules/ChromiumPdf.php b/src/Modules/ChromiumPdf.php index ba6676a..9d1e8d7 100644 --- a/src/Modules/ChromiumPdf.php +++ b/src/Modules/ChromiumPdf.php @@ -215,6 +215,27 @@ public function metadata(array $metadata): self return $this; } + /** + * Defines whether the resulting PDF should be flattened. + */ + public function flatten(): self + { + $this->formValue('flatten', true); + + return $this; + } + + /** + * Defines whether the resulting PDF should be encrypted. + */ + public function encrypt(string $userPassword, string $ownerPassword = ''): self + { + $this->formValue('userPassword', $userPassword); + $this->formValue('ownerPassword', $ownerPassword); + + return $this; + } + /** * Converts a target URL to PDF. * diff --git a/src/Modules/LibreOffice.php b/src/Modules/LibreOffice.php index e5b9adf..9bace77 100644 --- a/src/Modules/LibreOffice.php +++ b/src/Modules/LibreOffice.php @@ -358,6 +358,17 @@ public function flatten(): self return $this; } + /** + * Defines whether the resulting PDF should be encrypted. + */ + public function encrypt(string $userPassword, string $ownerPassword = ''): self + { + $this->formValue('userPassword', $userPassword); + $this->formValue('ownerPassword', $ownerPassword); + + return $this; + } + /** * Converts the given document(s) to PDF(s). Gotenberg will return either * a unique PDF if you request a merge or a ZIP archive with the PDFs. diff --git a/src/Modules/PdfEngines.php b/src/Modules/PdfEngines.php index 3290f7d..983d8d9 100644 --- a/src/Modules/PdfEngines.php +++ b/src/Modules/PdfEngines.php @@ -81,6 +81,18 @@ public function flattening(): self return $this; } + /** + * Defines whether the resulting PDF should be encrypted. + * Prefer the encrypt method if you only want to encrypt one or more PDFs. + */ + public function encrypting(string $userPassword, string $ownerPassword = ''): self + { + $this->formValue('userPassword', $userPassword); + $this->formValue('ownerPassword', $ownerPassword); + + return $this; + } + /** * Merges PDFs into a unique PDF. * @@ -186,4 +198,22 @@ public function writeMetadata(array $metadata, Stream ...$pdfs): RequestInterfac return $this->request(); } + + /** + * Allows encrypting one or more PDF. + * + * @throws NativeFunctionErrored + */ + public function encrypt(string $userPassword, string $ownerPassword = '', Stream ...$pdfs): RequestInterface + { + $this->encrypting($userPassword, $ownerPassword); + + foreach ($pdfs as $pdf) { + $this->formFile($pdf->getFilename(), $pdf->getStream()); + } + + $this->endpoint = '/forms/pdfengines/encrypt'; + + return $this->request(); + } } diff --git a/tests/Modules/ChromiumPdfTest.php b/tests/Modules/ChromiumPdfTest.php index 68c8f65..b219c63 100644 --- a/tests/Modules/ChromiumPdfTest.php +++ b/tests/Modules/ChromiumPdfTest.php @@ -53,6 +53,9 @@ function ( string|null $pdfa = null, bool $pdfua = false, array $metadata = [], + bool $flatten = false, + string $userPassword = '', + string $ownerPassword = '', array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -90,6 +93,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); @@ -133,6 +139,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); }, @@ -179,6 +188,9 @@ function ( 'PDF/A-1a', true, [ 'Producer' => 'Gotenberg' ], + true, + 'my_user_password', + 'my_owner_password', [ Stream::string('my.jpg', 'Image content'), ], @@ -229,6 +241,9 @@ function ( string|null $pdfa = null, bool $pdfua = false, array $metadata = [], + bool $flatten = false, + string $userPassword = '', + string $ownerPassword = '', array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -266,6 +281,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); @@ -311,6 +329,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); }, @@ -356,6 +377,9 @@ function ( 'PDF/A-1a', true, [ 'Producer' => 'Gotenberg' ], + true, + 'my_user_password', + 'my_owner_password', [ Stream::string('my.jpg', 'Image content'), ], @@ -408,6 +432,9 @@ function ( string|null $pdfa = null, bool $pdfua = false, array $metadata = [], + bool $flatten = false, + string $userPassword = '', + string $ownerPassword = '', array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -445,6 +472,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); @@ -495,6 +525,9 @@ function ( $pdfa, $pdfua, $metadata, + $flatten, + $userPassword, + $ownerPassword, $assets, ); }, @@ -549,6 +582,9 @@ function ( 'PDF/A-1a', true, [ 'Producer' => 'Gotenberg' ], + true, + 'my_user_password', + 'my_owner_password', [ Stream::string('my.jpg', 'Image content'), ], @@ -597,6 +633,9 @@ function hydrateChromiumPdfFormData( string|null $pdfa = null, bool $pdfua = false, array $metadata = [], + bool $flatten = false, + string $userPassword = '', + string $ownerPassword = '', array $assets = [], ): ChromiumPdf { if ($singlePage) { @@ -715,6 +754,14 @@ function hydrateChromiumPdfFormData( $chromium->metadata($metadata); } + if ($flatten) { + $chromium->flatten(); + } + + if ($userPassword !== '') { + $chromium->encrypt($userPassword, $ownerPassword); + } + if (count($assets) > 0) { $chromium->assets(...$assets); } @@ -764,6 +811,9 @@ function expectChromiumPdfOptions( string|null $pdfa, bool $pdfua, array $metadata, + bool $flatten, + string $userPassword, + string $ownerPassword, array $assets, ): void { expect($body)->unless($singlePage === false, fn ($body) => $body->toContainFormValue('singlePage', '1')); @@ -865,6 +915,10 @@ function expectChromiumPdfOptions( expect($body)->toContainFormValue('metadata', $json); } + expect($body)->unless($flatten === false, fn ($body) => $body->toContainFormValue('flatten', '1')); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('userPassword', $userPassword)); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('ownerPassword', $ownerPassword)); + if (count($assets) <= 0) { return; } diff --git a/tests/Modules/LibreOfficeTest.php b/tests/Modules/LibreOfficeTest.php index 4350468..4c7c38d 100644 --- a/tests/Modules/LibreOfficeTest.php +++ b/tests/Modules/LibreOfficeTest.php @@ -45,6 +45,8 @@ function ( array $metadata = [], bool $merge = false, bool $flatten = false, + string $userPassword = '', + string $ownerPassword = '', ): void { $libreOffice = Gotenberg::libreOffice(''); @@ -166,6 +168,10 @@ function ( $libreOffice->flatten(); } + if ($userPassword !== '') { + $libreOffice->encrypt($userPassword, $ownerPassword); + } + $request = $libreOffice->convert(...$files); $body = sanitize($request->getBody()->getContents()); @@ -204,6 +210,8 @@ function ( expect($body)->unless($pdfua === false, fn ($body) => $body->toContainFormValue('pdfua', '1')); expect($body)->unless($merge === false, fn ($body) => $body->toContainFormValue('merge', '1')); expect($body)->unless($flatten === false, fn ($body) => $body->toContainFormValue('flatten', '1')); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('userPassword', $userPassword)); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('ownerPassword', $ownerPassword)); if (count($metadata) > 0) { $json = json_encode($metadata); @@ -261,5 +269,7 @@ function ( [ 'Producer' => 'Gotenberg' ], true, true, + 'my_user_password', + 'my_owner_password', ], ]); diff --git a/tests/Modules/PdfEnginesTest.php b/tests/Modules/PdfEnginesTest.php index 8ab07ea..b0d22c8 100644 --- a/tests/Modules/PdfEnginesTest.php +++ b/tests/Modules/PdfEnginesTest.php @@ -14,7 +14,7 @@ * @param Stream[] $pdfs * @param array> $metadata */ - function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false): void { + function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = ''): void { $pdfEngines = Gotenberg::pdfEngines('')->index(new DummyIndex()); if ($pdfa !== null) { @@ -33,6 +33,10 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met $pdfEngines->flattening(); } + if ($userPassword !== '') { + $pdfEngines->encrypting($userPassword, $ownerPassword); + } + $request = $pdfEngines->merge(...$pdfs); $body = sanitize($request->getBody()->getContents()); @@ -50,6 +54,8 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met } expect($body)->unless($flatten === false, fn ($body) => $body->toContainFormValue('flatten', '1')); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('userPassword', $userPassword)); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('ownerPassword', $ownerPassword)); foreach ($pdfs as $pdf) { $pdf->getStream()->rewind(); @@ -73,13 +79,15 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met true, [ 'Producer' => 'Gotenberg' ], true, + 'my_user_password', + 'my_owner_password', ], ]); it( 'creates a valid request for the "/forms/pdfengines/split" endpoint', /** @param Stream[] $pdfs */ - function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false): void { + function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = ''): void { $pdfEngines = Gotenberg::pdfEngines(''); if ($pdfa !== null) { @@ -98,6 +106,10 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = $pdfEngines->flattening(); } + if ($userPassword !== '') { + $pdfEngines->encrypting($userPassword, $ownerPassword); + } + $request = $pdfEngines->split($mode, ...$pdfs); $body = sanitize($request->getBody()->getContents()); @@ -118,6 +130,8 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = } expect($body)->unless($flatten === false, fn ($body) => $body->toContainFormValue('flatten', '1')); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('userPassword', $userPassword)); + expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('ownerPassword', $ownerPassword)); foreach ($pdfs as $pdf) { $pdf->getStream()->rewind(); @@ -142,6 +156,8 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = true, [ 'Producer' => 'Gotenberg' ], true, + 'my_user_password', + 'my_owner_password', ], ]); @@ -266,3 +282,32 @@ function (array $metadata, array $pdfs): void { ], ], ]); + +it( + 'creates a valid request for the "/forms/pdfengines/encrypt" endpoint', + /** @param Stream[] $pdfs */ + function (string $userPassword, string $ownerPassword, array $pdfs): void { + $pdfEngines = Gotenberg::pdfEngines(''); + + $request = $pdfEngines->encrypt($userPassword, $ownerPassword, ...$pdfs); + $body = sanitize($request->getBody()->getContents()); + + expect($request->getUri()->getPath())->toBe('/forms/pdfengines/encrypt'); + expect($body)->toContainFormValue('userPassword', $userPassword); + expect($body)->toContainFormValue('ownerPassword', $ownerPassword); + + foreach ($pdfs as $pdf) { + $pdf->getStream()->rewind(); + expect($body)->toContainFormFile($pdf->getFilename(), $pdf->getStream()->getContents(), 'application/pdf'); + } + }, +)->with([ + [ + 'my_user_password', + 'my_owner_password', + [ + Stream::string('my.pdf', 'PDF content'), + Stream::string('my_second.pdf', 'Second PDF content'), + ], + ], +]); From 09383b7e88c26a40e1700c30aacd1b0050e4c258 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Thu, 13 Nov 2025 19:20:54 +0100 Subject: [PATCH 3/4] docs(README): add screenshot image formats --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebd1571..50c1288 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ $filename = Gotenberg::save($request, '/path/to/saving/directory'); ``` It returns the filename of the resulting file. By default, Gotenberg creates a *UUID* filename (i.e., -`95cd9945-484f-4f89-8bdb-23dbdd0bdea9`) with either a `.zip` or a `.pdf` file extension. +`95cd9945-484f-4f89-8bdb-23dbdd0bdea9`) with either a `.zip` or a `.pdf` file extension (or image formats for screenshots). You may also explicitly set the HTTP client: From eceec25f25f6340d541eafbf31a7e06eb7ef5cf6 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Thu, 13 Nov 2025 20:43:21 +0100 Subject: [PATCH 4/4] feat: add embed feature --- src/DownloadFrom.php | 4 +- src/Modules/ChromiumPdf.php | 12 +++++ src/Modules/LibreOffice.php | 12 +++++ src/Modules/PdfEngines.php | 32 ++++++++++++ src/MultipartFormDataModule.php | 4 +- tests/Modules/ChromiumPdfTest.php | 37 ++++++++++++++ tests/Modules/LibreOfficeTest.php | 15 ++++++ tests/Modules/PdfEnginesTest.php | 73 +++++++++++++++++++++++++-- tests/MultipartFormDataModuleTest.php | 4 +- tests/Pest.php | 4 +- 10 files changed, 187 insertions(+), 10 deletions(-) diff --git a/src/DownloadFrom.php b/src/DownloadFrom.php index acc1740..3d0cddb 100644 --- a/src/DownloadFrom.php +++ b/src/DownloadFrom.php @@ -12,14 +12,16 @@ class DownloadFrom implements JsonSerializable public function __construct( public readonly string $url, public readonly array|null $extraHttpHeaders = null, + public readonly bool $embedded = false, ) { } - /** @return array> */ + /** @return array|bool|string> */ public function jsonSerialize(): array { $serialized = [ 'url' => $this->url, + 'embedded' => $this->embedded, ]; if (! empty($this->extraHttpHeaders)) { diff --git a/src/Modules/ChromiumPdf.php b/src/Modules/ChromiumPdf.php index 9d1e8d7..e1dac3b 100644 --- a/src/Modules/ChromiumPdf.php +++ b/src/Modules/ChromiumPdf.php @@ -236,6 +236,18 @@ public function encrypt(string $userPassword, string $ownerPassword = ''): self return $this; } + /** + * Sets the file to embed in the resulting PDF. + */ + public function embeds(Stream ...$embeds): self + { + foreach ($embeds as $embed) { + $this->formFile($embed->getFilename(), $embed->getStream(), 'embeds'); + } + + return $this; + } + /** * Converts a target URL to PDF. * diff --git a/src/Modules/LibreOffice.php b/src/Modules/LibreOffice.php index 9bace77..4ebc47e 100644 --- a/src/Modules/LibreOffice.php +++ b/src/Modules/LibreOffice.php @@ -369,6 +369,18 @@ public function encrypt(string $userPassword, string $ownerPassword = ''): self return $this; } + /** + * Sets the file to embed in the resulting PDF. + */ + public function embeds(Stream ...$embeds): self + { + foreach ($embeds as $embed) { + $this->formFile($embed->getFilename(), $embed->getStream(), 'embeds'); + } + + return $this; + } + /** * Converts the given document(s) to PDF(s). Gotenberg will return either * a unique PDF if you request a merge or a ZIP archive with the PDFs. diff --git a/src/Modules/PdfEngines.php b/src/Modules/PdfEngines.php index 983d8d9..9e107d2 100644 --- a/src/Modules/PdfEngines.php +++ b/src/Modules/PdfEngines.php @@ -93,6 +93,18 @@ public function encrypting(string $userPassword, string $ownerPassword = ''): se return $this; } + /** + * Sets the file to embed in the resulting PDF. + */ + public function embeds(Stream ...$embeds): self + { + foreach ($embeds as $embed) { + $this->formFile($embed->getFilename(), $embed->getStream(), 'embeds'); + } + + return $this; + } + /** * Merges PDFs into a unique PDF. * @@ -216,4 +228,24 @@ public function encrypt(string $userPassword, string $ownerPassword = '', Stream return $this->request(); } + + /** + * Allows embedding one or more files to one or more PDF. + * + * @param Stream[] $embeds + * + * @throws NativeFunctionErrored + */ + public function embed(array $embeds, Stream ...$pdfs): RequestInterface + { + foreach ($pdfs as $pdf) { + $this->formFile($pdf->getFilename(), $pdf->getStream()); + } + + $this->embeds(...$embeds); + + $this->endpoint = '/forms/pdfengines/embed'; + + return $this->request(); + } } diff --git a/src/MultipartFormDataModule.php b/src/MultipartFormDataModule.php index e0c54cd..80087b8 100644 --- a/src/MultipartFormDataModule.php +++ b/src/MultipartFormDataModule.php @@ -118,10 +118,10 @@ protected function formValue(string $name, mixed $value): self return $this; } - protected function formFile(string $filename, StreamInterface $stream): void + protected function formFile(string $filename, StreamInterface $stream, string $name = 'files'): void { $this->multipartFormData[] = [ - 'name' => 'files', + 'name' => $name, 'filename' => $filename, 'contents' => $stream, ]; diff --git a/tests/Modules/ChromiumPdfTest.php b/tests/Modules/ChromiumPdfTest.php index b219c63..e0c467b 100644 --- a/tests/Modules/ChromiumPdfTest.php +++ b/tests/Modules/ChromiumPdfTest.php @@ -17,6 +17,7 @@ * @param int[] $failOnHttpStatusCodes * @param int[] $failOnResourceHttpStatusCodes * @param array> $metadata + * @param Stream[] $embeds * @param Stream[] $assets */ function ( @@ -56,6 +57,7 @@ function ( bool $flatten = false, string $userPassword = '', string $ownerPassword = '', + array $embeds = [], array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -96,6 +98,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); @@ -142,6 +145,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); }, @@ -191,6 +195,10 @@ function ( true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], [ Stream::string('my.jpg', 'Image content'), ], @@ -205,6 +213,7 @@ function ( * @param int[] $failOnHttpStatusCodes * @param int[] $failOnResourceHttpStatusCodes * @param array> $metadata + * @param Stream[] $embeds * @param Stream[] $assets */ function ( @@ -244,6 +253,7 @@ function ( bool $flatten = false, string $userPassword = '', string $ownerPassword = '', + array $embeds = [], array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -284,6 +294,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); @@ -332,6 +343,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); }, @@ -380,6 +392,10 @@ function ( true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], [ Stream::string('my.jpg', 'Image content'), ], @@ -395,6 +411,7 @@ function ( * @param int[] $failOnResourceHttpStatusCodes * @param Stream[] $markdowns * @param array> $metadata + * @param Stream[] $embeds * @param Stream[] $assets */ function ( @@ -435,6 +452,7 @@ function ( bool $flatten = false, string $userPassword = '', string $ownerPassword = '', + array $embeds = [], array $assets = [], ): void { $chromium = Gotenberg::chromium('')->pdf(); @@ -475,6 +493,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); @@ -528,6 +547,7 @@ function ( $flatten, $userPassword, $ownerPassword, + $embeds, $assets, ); }, @@ -585,6 +605,10 @@ function ( true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], [ Stream::string('my.jpg', 'Image content'), ], @@ -597,6 +621,7 @@ function ( * @param int[] $failOnHttpStatusCodes * @param int[] $failOnResourceHttpStatusCodes * @param array> $metadata + * @param Stream[] $embeds * @param Stream[] $assets */ function hydrateChromiumPdfFormData( @@ -636,6 +661,7 @@ function hydrateChromiumPdfFormData( bool $flatten = false, string $userPassword = '', string $ownerPassword = '', + array $embeds = [], array $assets = [], ): ChromiumPdf { if ($singlePage) { @@ -762,6 +788,10 @@ function hydrateChromiumPdfFormData( $chromium->encrypt($userPassword, $ownerPassword); } + if (count($embeds) > 0) { + $chromium->embeds(...$embeds); + } + if (count($assets) > 0) { $chromium->assets(...$assets); } @@ -775,6 +805,7 @@ function hydrateChromiumPdfFormData( * @param int[] $failOnHttpStatusCodes * @param int[] $failOnResourceHttpStatusCodes * @param array> $metadata + * @param Stream[] $embeds * @param Stream[] $assets */ function expectChromiumPdfOptions( @@ -814,6 +845,7 @@ function expectChromiumPdfOptions( bool $flatten, string $userPassword, string $ownerPassword, + array $embeds, array $assets, ): void { expect($body)->unless($singlePage === false, fn ($body) => $body->toContainFormValue('singlePage', '1')); @@ -919,6 +951,11 @@ function expectChromiumPdfOptions( expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('userPassword', $userPassword)); expect($body)->unless($userPassword === '', fn ($body) => $body->toContainFormValue('ownerPassword', $ownerPassword)); + foreach ($embeds as $embed) { + $embed->getStream()->rewind(); + expect($body)->toContainFormFile($embed->getFilename(), $embed->getStream()->getContents(), 'application/xml', 'embeds'); + } + if (count($assets) <= 0) { return; } diff --git a/tests/Modules/LibreOfficeTest.php b/tests/Modules/LibreOfficeTest.php index 4c7c38d..2078973 100644 --- a/tests/Modules/LibreOfficeTest.php +++ b/tests/Modules/LibreOfficeTest.php @@ -13,6 +13,7 @@ /** * @param Stream[] $files * @param array> $metadata + * @param Stream[] $embeds */ function ( array $files, @@ -47,6 +48,7 @@ function ( bool $flatten = false, string $userPassword = '', string $ownerPassword = '', + array $embeds = [], ): void { $libreOffice = Gotenberg::libreOffice(''); @@ -172,6 +174,10 @@ function ( $libreOffice->encrypt($userPassword, $ownerPassword); } + if (count($embeds) > 0) { + $libreOffice->embeds(...$embeds); + } + $request = $libreOffice->convert(...$files); $body = sanitize($request->getBody()->getContents()); @@ -228,6 +234,11 @@ function ( expect($body)->toContainFormFile($filename, $file->getStream()->getContents(), 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); } + + foreach ($embeds as $embed) { + $embed->getStream()->rewind(); + expect($body)->toContainFormFile($embed->getFilename(), $embed->getStream()->getContents(), 'application/xml', 'embeds'); + } }, )->with([ [ @@ -271,5 +282,9 @@ function ( true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], ], ]); diff --git a/tests/Modules/PdfEnginesTest.php b/tests/Modules/PdfEnginesTest.php index b0d22c8..936e781 100644 --- a/tests/Modules/PdfEnginesTest.php +++ b/tests/Modules/PdfEnginesTest.php @@ -13,8 +13,9 @@ /** * @param Stream[] $pdfs * @param array> $metadata + * @param Stream[] $embeds */ - function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = ''): void { + function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = '', array $embeds = []): void { $pdfEngines = Gotenberg::pdfEngines('')->index(new DummyIndex()); if ($pdfa !== null) { @@ -37,6 +38,10 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met $pdfEngines->encrypting($userPassword, $ownerPassword); } + if (count($embeds) > 0) { + $pdfEngines->embeds(...$embeds); + } + $request = $pdfEngines->merge(...$pdfs); $body = sanitize($request->getBody()->getContents()); @@ -61,6 +66,11 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met $pdf->getStream()->rewind(); expect($body)->toContainFormFile('foo_' . $pdf->getFilename(), $pdf->getStream()->getContents(), 'application/pdf'); } + + foreach ($embeds as $embed) { + $embed->getStream()->rewind(); + expect($body)->toContainFormFile($embed->getFilename(), $embed->getStream()->getContents(), 'application/xml', 'embeds'); + } }, )->with([ [ @@ -81,13 +91,20 @@ function (array $pdfs, string|null $pdfa = null, bool $pdfua = false, array $met true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], ], ]); it( 'creates a valid request for the "/forms/pdfengines/split" endpoint', - /** @param Stream[] $pdfs */ - function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = ''): void { + /** + * @param Stream[] $pdfs + * @param Stream[] $embeds + */ + function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = false, array $metadata = [], bool $flatten = false, string $userPassword = '', string $ownerPassword = '', array $embeds = []): void { $pdfEngines = Gotenberg::pdfEngines(''); if ($pdfa !== null) { @@ -110,6 +127,10 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = $pdfEngines->encrypting($userPassword, $ownerPassword); } + if (count($embeds) > 0) { + $pdfEngines->embeds(...$embeds); + } + $request = $pdfEngines->split($mode, ...$pdfs); $body = sanitize($request->getBody()->getContents()); @@ -137,6 +158,11 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = $pdf->getStream()->rewind(); expect($body)->toContainFormFile($pdf->getFilename(), $pdf->getStream()->getContents(), 'application/pdf'); } + + foreach ($embeds as $embed) { + $embed->getStream()->rewind(); + expect($body)->toContainFormFile($embed->getFilename(), $embed->getStream()->getContents(), 'application/xml', 'embeds'); + } }, )->with([ [ @@ -158,6 +184,10 @@ function (array $pdfs, SplitMode $mode, string|null $pdfa = null, bool $pdfua = true, 'my_user_password', 'my_owner_password', + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], ], ]); @@ -311,3 +341,40 @@ function (string $userPassword, string $ownerPassword, array $pdfs): void { ], ], ]); + +it( + 'creates a valid request for the "/forms/pdfengines/embed" endpoint', + /** + * @param Stream[] $pdfs + * @param Stream[] $embeds + */ + function (array $embeds, array $pdfs): void { + $pdfEngines = Gotenberg::pdfEngines(''); + + $request = $pdfEngines->embed($embeds, ...$pdfs); + $body = sanitize($request->getBody()->getContents()); + + expect($request->getUri()->getPath())->toBe('/forms/pdfengines/embed'); + + foreach ($pdfs as $pdf) { + $pdf->getStream()->rewind(); + expect($body)->toContainFormFile($pdf->getFilename(), $pdf->getStream()->getContents(), 'application/pdf'); + } + + foreach ($embeds as $embed) { + $embed->getStream()->rewind(); + expect($body)->toContainFormFile($embed->getFilename(), $embed->getStream()->getContents(), 'application/xml', 'embeds'); + } + }, +)->with([ + [ + [ + Stream::string('my.xml', 'XML content'), + Stream::string('my_second.xml', 'Second XML content'), + ], + [ + Stream::string('my.pdf', 'PDF content'), + Stream::string('my_second.pdf', 'Second PDF content'), + ], + ], +]); diff --git a/tests/MultipartFormDataModuleTest.php b/tests/MultipartFormDataModuleTest.php index ec48438..19437f0 100644 --- a/tests/MultipartFormDataModuleTest.php +++ b/tests/MultipartFormDataModuleTest.php @@ -14,7 +14,7 @@ function (): void { ->outputFilename('my_filename') ->downloadFrom([ new DownloadFrom('https://my.url/my_filename'), - new DownloadFrom('https://my.url/my_filename_2', ['X-Header' => 'value']), + new DownloadFrom('https://my.url/my_filename_2', ['X-Header' => 'value'], true), ]) ->webhook('https://my.webhook.url', 'https://my.webhook.error.url') ->webhookMethod('POST') @@ -27,7 +27,7 @@ function (): void { expect($request->getHeader('Gotenberg-Output-Filename'))->toMatchArray(['my_filename']); - expect(sanitize($request->getBody()->getContents()))->toContainFormValue('downloadFrom', '[{"url":"https:\/\/my.url\/my_filename"},{"url":"https:\/\/my.url\/my_filename_2","extraHttpHeaders":{"X-Header":"value"}}]'); + expect(sanitize($request->getBody()->getContents()))->toContainFormValue('downloadFrom', '[{"url":"https:\/\/my.url\/my_filename","embedded":false},{"url":"https:\/\/my.url\/my_filename_2","embedded":true,"extraHttpHeaders":{"X-Header":"value"}}]'); expect($request->getHeader('Gotenberg-Webhook-Url'))->toMatchArray(['https://my.webhook.url']); expect($request->getHeader('Gotenberg-Webhook-Error-Url'))->toMatchArray(['https://my.webhook.error.url']); diff --git a/tests/Pest.php b/tests/Pest.php index ddb7354..0f7e7a3 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -22,10 +22,10 @@ function sanitize(string $body): string ); }); -expect()->extend('toContainFormFile', function (string $filename, string $content, string|null $contentType = null) { +expect()->extend('toContainFormFile', function (string $filename, string $content, string|null $contentType = null, string $fieldName = 'files') { $length = mb_strlen($content); - $needle = 'Content-Disposition: form-data; name="files"; filename="' . $filename . '" Content-Length: ' . $length; + $needle = 'Content-Disposition: form-data; name="' . $fieldName . '"; filename="' . $filename . '" Content-Length: ' . $length; if ($contentType !== null) { $needle .= ' Content-Type: ' . $contentType; }