├── LICENSE
├── README.md
├── composer.json
├── phpstan.neon
└── src
├── ApiModule.php
├── DownloadFrom.php
├── Exceptions
├── GotenbergApiErrored.php
├── NativeFunctionErrored.php
└── NoOutputFileInResponse.php
├── Gotenberg.php
├── HrtimeIndex.php
├── Index.php
├── Modules
├── Chromium.php
├── ChromiumCookie.php
├── ChromiumMultipartFormDataModule.php
├── ChromiumPdf.php
├── ChromiumScreenshot.php
├── LibreOffice.php
└── PdfEngines.php
├── MultipartFormDataModule.php
├── SplitMode.php
└── Stream.php
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Julien Neuhart
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Gotenberg PHP
4 |
A PHP client for interacting with Gotenberg
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ---
15 |
16 | This package is a PHP client for [Gotenberg](https://gotenberg.dev), a developer-friendly API to interact with powerful
17 | tools like Chromium and LibreOffice for converting numerous document formats (HTML, Markdown, Word, Excel, etc.) into
18 | PDF files, and more!
19 |
20 | | Gotenberg version | Client |
21 | |---------------------|---------------------------------------------------------------------------------------------------|
22 | | `8.x` **(current)** | `v2.x` **(current)** |
23 | | `7.x` | `v1.x` |
24 | | `6.x` | [thecodingmachine/gotenberg-php-client](https://github.com/thecodingmachine/gotenberg-php-client) |
25 |
26 | > [!TIP]
27 | > An experimental [Symfony Bundle](https://github.com/sensiolabs/GotenbergBundle) is also available!
28 |
29 | ## Quick Examples
30 |
31 | You may convert a target URL to PDF and save it to a given directory:
32 |
33 | ```php
34 | use Gotenberg\Gotenberg;
35 |
36 | // Converts a target URL to PDF and saves it to a given directory.
37 | $filename = Gotenberg::save(
38 | Gotenberg::chromium($apiUrl)->pdf()->url('https://my.url'),
39 | $pathToSavingDirectory
40 | );
41 | ```
42 |
43 | You may also convert Office documents:
44 |
45 | ```php
46 | use Gotenberg\Gotenberg;
47 | use Gotenberg\Stream;
48 |
49 | // Converts Office documents to PDF.
50 | $response = Gotenberg::send(
51 | Gotenberg::libreOffice($apiUrl)
52 | ->convert(
53 | Stream::path($pathToDocx),
54 | Stream::path($pathToXlsx)
55 | )
56 | );
57 | ```
58 |
59 | ## Requirement
60 |
61 | This packages requires [Gotenberg](https://gotenberg.dev), a containerized API for seamless PDF conversion.
62 |
63 | See the [installation guide](https://gotenberg.dev/docs/getting-started/installation) for more information.
64 |
65 | ## Installation
66 |
67 | This package can be installed with Composer:
68 |
69 | ```
70 | composer require gotenberg/gotenberg-php
71 | ```
72 |
73 | We use *PSR-7* HTTP message interfaces (i.e., `RequestInterface` and `ResponseInterface`) and the *PSR-18* HTTP client
74 | interface (i.e., `ClientInterface`).
75 |
76 | For the latter, you may need an adapter in order to use your favorite client library. Check the available adapters:
77 |
78 | * https://docs.php-http.org/en/latest/clients.html
79 |
80 | If you're not sure which adapter you should use, consider using the `php-http/guzzle7-adapter`:
81 |
82 | ```
83 | composer require php-http/guzzle7-adapter
84 | ```
85 |
86 | ## Build a request
87 |
88 | This package is organized around *modules*, namely:
89 |
90 | ```php
91 | use Gotenberg\Gotenberg;
92 |
93 | Gotenberg::chromium($apiUrl);
94 | Gotenberg::libreOffice($apiUrl);
95 | Gotenberg::pdfEngines($apiUrl);
96 | ```
97 |
98 | Each of these modules offers a variety of methods to populate a *multipart/form-data* request.
99 |
100 | After setting all optional form fields and files, you can create a request by calling the method that represents the endpoint.
101 | For example, to call the `/forms/chromium/convert/url` route:
102 |
103 | ```php
104 | use Gotenberg\Gotenberg;
105 |
106 | Gotenberg::chromium($apiUrl)
107 | ->pdf() // Or screenshot().
108 | ->singlePage() // Optional.
109 | ->url('https://my.url'));
110 | ```
111 |
112 | > [!TIP]
113 | > Head to the [documentation](https://gotenberg.dev/) to learn about all possibilities.
114 |
115 | If the route requires form files, use the `Stream` class to create them:
116 |
117 | ```php
118 | use Gotenberg\DownloadFrom;
119 | use Gotenberg\Gotenberg;
120 | use Gotenberg\Stream;
121 |
122 | Gotenberg::libreOffice($apiUrl)
123 | ->convert(Stream::path($pathToDocument));
124 |
125 | // Alternatively, you may also set the content directly.
126 | Gotenberg::chromium($apiUrl)
127 | ->pdf()
128 | ->assets(Stream::string('style.css', 'body{font-family: Arial, Helvetica, sans-serif;}'))
129 | ->html(Stream::string('index.html', '
Hello, world!
'));
130 |
131 | // Or create your stream from scratch.
132 | Gotenberg::libreOffice($apiUrl)
133 | ->convert(new Stream('document.docx', $stream));
134 |
135 | // Or even tell Gotenberg to download the files for you.
136 | Gotenberg::libreOffice($apiUrl)
137 | ->downloadFrom([
138 | new DownloadFrom('https://url.to.document.docx', ['MyHeader' => 'MyValue'])
139 | ])
140 | ->convert();
141 | ```
142 |
143 | ## Send a request to the API
144 |
145 | After having created the HTTP request, you have two options:
146 |
147 | 1. Get the response from the API and handle it according to your need.
148 | 2. Save the resulting file to a given directory.
149 |
150 | ### Get a response
151 |
152 | You may use any HTTP client that is able to handle a *PSR-7* `RequestInterface` to call the API:
153 |
154 | ```php
155 | use Gotenberg\Gotenberg;
156 |
157 | $request = Gotenberg::chromium($apiUrl)
158 | ->pdf()
159 | ->url('https://my.url');
160 |
161 | $response = $client->sendRequest($request);
162 | ```
163 |
164 | If you have a *PSR-18* compatible HTTP client (see [Installation](#installation)), you may also use `Gotenberg::send`:
165 |
166 | ```php
167 | use Gotenberg\Gotenberg;
168 |
169 | $request = Gotenberg::chromium($apiUrl)
170 | ->pdf()
171 | ->url('https://my.url');
172 |
173 | try {
174 | $response = Gotenberg::send($request);
175 | return $response;
176 | } catch (GotenbergApiErrored $e) {
177 | // $e->getResponse();
178 | }
179 | ```
180 |
181 | This helper will parse the response and if it is not **2xx**, it will throw an exception. That's especially useful if
182 | you wish to return the response directly to the browser.
183 |
184 | You may also explicitly set the HTTP client:
185 |
186 | ```php
187 | use Gotenberg\Gotenberg;
188 |
189 | $response = Gotenberg::send($request, $client);
190 | ```
191 |
192 | ### Save the resulting file
193 |
194 | If you have a *PSR-18* compatible HTTP client (see [Installation](#installation)), you may use `Gotenberg::save`:
195 |
196 | ```php
197 | use Gotenberg\Gotenberg;
198 |
199 | $request = Gotenberg::chromium($apiUrl)
200 | ->pdf()
201 | ->url('https://my.url');
202 |
203 | $filename = Gotenberg::save($request, '/path/to/saving/directory');
204 | ```
205 |
206 | It returns the filename of the resulting file. By default, Gotenberg creates a *UUID* filename (i.e.,
207 | `95cd9945-484f-4f89-8bdb-23dbdd0bdea9`) with either a `.zip` or a `.pdf` file extension.
208 |
209 | You may also explicitly set the HTTP client:
210 |
211 | ```php
212 | use Gotenberg\Gotenberg;
213 |
214 | $response = Gotenberg::save($request, $pathToSavingDirectory, $client);
215 | ```
216 |
217 | ### Filename
218 |
219 | You may override the output filename with:
220 |
221 | ```php
222 | use Gotenberg\Gotenberg;
223 |
224 | $request = Gotenberg::chromium($apiUrl)
225 | ->pdf()
226 | ->outputFilename('my_file')
227 | ->url('https://my.url');
228 | ```
229 |
230 | Gotenberg will automatically add the correct file extension.
231 |
232 | ### Trace or request ID
233 |
234 | By default, Gotenberg creates a *UUID* trace that identifies a request in its logs. You may override its value thanks to:
235 |
236 | ```php
237 | use Gotenberg\Gotenberg;
238 |
239 | $request = Gotenberg::chromium('$apiUrl')
240 | ->pdf()
241 | ->trace('debug')
242 | ->url('https://my.url');
243 | ```
244 |
245 | It will set the header `Gotenberg-Trace` with your value. You may also override the default header name:
246 |
247 | ```php
248 | use Gotenberg\Gotenberg;
249 |
250 | $request = Gotenberg::chromium($apiUrl)
251 | ->pdf()
252 | ->trace('debug', 'Request-Id')
253 | ->url('https://my.url');
254 | ```
255 |
256 | Please note that it should be the same value as defined by the `--api-trace-header` Gotenberg's property.
257 |
258 | The response from Gotenberg will also contain the trace header. In case of error, both the `Gotenberg::send` and
259 | `Gotenberg::save` methods throw a `GotenbergApiErrored` exception that provides the following method for retrieving the
260 | trace:
261 |
262 | ```php
263 | use Gotenberg\Exceptions\GotenbergApiErrored;
264 | use Gotenberg\Gotenberg;
265 |
266 | try {
267 | $response = Gotenberg::send(
268 | Gotenberg::chromium($apiUrl)
269 | ->screenshot()
270 | ->url('https://my.url')
271 | );
272 | } catch (GotenbergApiErrored $e) {
273 | $trace = $e->getGotenbergTrace();
274 | // Or if you override the header name:
275 | $trace = $e->getGotenbergTrace('Request-Id');
276 | }
277 | ```
278 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gotenberg/gotenberg-php",
3 | "description": "A PHP client for interacting with Gotenberg, a developer-friendly API for converting numerous document formats into PDF files, and more!",
4 | "homepage": "https://github.com/gotenberg/gotenberg-php",
5 | "keywords": [
6 | "gotenberg",
7 | "pdf",
8 | "convert",
9 | "html",
10 | "markdown",
11 | "csv",
12 | "excel",
13 | "word",
14 | "xlsx",
15 | "docx",
16 | "pptx",
17 | "pdftk",
18 | "libreoffice",
19 | "unoconv",
20 | "wkhtmltopdf",
21 | "unoconv",
22 | "chrome",
23 | "chromium",
24 | "puppeteer"
25 | ],
26 | "license": "MIT",
27 | "authors": [
28 | {
29 | "name": "Julien Neuhart",
30 | "email": "neuhart.julien@gmail.com",
31 | "homepage": "https://github.com/gulien",
32 | "role": "Developer"
33 | }
34 | ],
35 | "require": {
36 | "php": "^8.1|^8.2|^8.3|^8.4",
37 | "ext-json": "*",
38 | "ext-mbstring": "*",
39 | "guzzlehttp/psr7": "^1 || ^2.1",
40 | "php-http/discovery": "^1.14",
41 | "psr/http-client": "^1.0",
42 | "psr/http-message": "^1.0|^2.0"
43 | },
44 | "require-dev": {
45 | "doctrine/coding-standard": "^12.0",
46 | "pestphp/pest": "^2.28",
47 | "phpstan/phpstan": "^1.12",
48 | "squizlabs/php_codesniffer": "^3.10"
49 | },
50 | "autoload": {
51 | "psr-4": {
52 | "Gotenberg\\": "src"
53 | }
54 | },
55 | "autoload-dev": {
56 | "psr-4": {
57 | "Gotenberg\\Test\\": "tests"
58 | }
59 | },
60 | "scripts": {
61 | "lint": [
62 | "phpcs",
63 | "phpstan analyse -l max src tests"
64 | ],
65 | "lint:fix": "phpcbf",
66 | "tests": "XDEBUG_MODE=coverage pest --coverage --coverage-html coverage_html --coverage-clover coverage.xml",
67 | "all": [
68 | "@composer run lint:fix",
69 | "@composer run lint",
70 | "@composer run tests"
71 | ]
72 | },
73 | "config": {
74 | "sort-packages": true,
75 | "allow-plugins": {
76 | "dealerdirect/phpcodesniffer-composer-installer": true,
77 | "pestphp/pest-plugin": true,
78 | "php-http/discovery": true
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | ignoreErrors:
3 | -
4 | message: '#Call to an undefined method Pest\\Expectation::toContainFormValue\(\).#'
5 | path: tests/*
6 | -
7 | message: '#Call to an undefined method Pest\\Expectation::toContainFormFile\(\).#'
8 | path: tests/*
9 | -
10 | message: '#Undefined variable: \$this#'
11 | path: tests/*
--------------------------------------------------------------------------------
/src/ApiModule.php:
--------------------------------------------------------------------------------
1 | */
18 | protected array $headers = [];
19 |
20 | public function __construct(string $baseUrl)
21 | {
22 | if (mb_substr($baseUrl, -1) === '/') {
23 | // We remove the trailing slash as the endpoints start with a slash.
24 | $this->url = mb_substr($baseUrl, 0, -1);
25 |
26 | return;
27 | }
28 |
29 | $this->url = $baseUrl;
30 | }
31 |
32 | /**
33 | * Overrides the default UUID trace, or request ID, that identifies a
34 | * request in Gotenberg's logs.
35 | */
36 | public function trace(string $trace, string $header = 'Gotenberg-Trace'): self
37 | {
38 | $this->headers[$header] = $trace;
39 |
40 | return $this;
41 | }
42 |
43 | protected function request(string $method, StreamInterface|null $body = null): RequestInterface
44 | {
45 | $request = Psr17FactoryDiscovery::findRequestFactory()
46 | ->createRequest($method, $this->url . $this->endpoint);
47 |
48 | if ($body !== null) {
49 | $request->withBody($body);
50 | }
51 |
52 | foreach ($this->headers as $key => $value) {
53 | $request = $request->withHeader($key, $value);
54 | }
55 |
56 | return $request;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/DownloadFrom.php:
--------------------------------------------------------------------------------
1 | $extraHttpHeaders */
12 | public function __construct(
13 | public readonly string $url,
14 | public readonly array|null $extraHttpHeaders = null,
15 | ) {
16 | }
17 |
18 | /** @return array> */
19 | public function jsonSerialize(): array
20 | {
21 | $serialized = [
22 | 'url' => $this->url,
23 | ];
24 |
25 | if (! empty($this->extraHttpHeaders)) {
26 | $serialized['extraHttpHeaders'] = $this->extraHttpHeaders;
27 | }
28 |
29 | return $serialized;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Exceptions/GotenbergApiErrored.php:
--------------------------------------------------------------------------------
1 | getBody()->getContents(), $response->getStatusCode());
17 | $exception->response = $response;
18 | $exception->response->getBody()->rewind();
19 |
20 | return $exception;
21 | }
22 |
23 | public function getGotenbergTrace(string $header = 'Gotenberg-Trace'): string
24 | {
25 | return $this->response->getHeaderLine($header);
26 | }
27 |
28 | public function getResponse(): ResponseInterface
29 | {
30 | return $this->response;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Exceptions/NativeFunctionErrored.php:
--------------------------------------------------------------------------------
1 | sendRequest($request);
53 |
54 | if ($response->getStatusCode() < 200 || $response->getStatusCode() > 299) {
55 | throw GotenbergApiErrored::createFromResponse($response);
56 | }
57 |
58 | return $response;
59 | }
60 |
61 | /**
62 | * Handles a request to Gotenberg and saves the output file if any.
63 | * On success, returns the filename based on the 'Content-Disposition' header.
64 | *
65 | * @throws GotenbergApiErrored
66 | * @throws NoOutputFileInResponse
67 | * @throws NativeFunctionErrored
68 | */
69 | public static function save(RequestInterface $request, string $dirPath, ClientInterface|null $client = null): string
70 | {
71 | $response = self::send($request, $client);
72 |
73 | $contentDisposition = $response->getHeader('Content-Disposition');
74 | if (count($contentDisposition) === 0) {
75 | throw new NoOutputFileInResponse();
76 | }
77 |
78 | $filename = false;
79 | foreach ($contentDisposition as $value) {
80 | if (preg_match('/filename="(.+?)"/', $value, $matches)) {
81 | $filename = $matches[1];
82 | break;
83 | }
84 |
85 | if (preg_match('/filename=([^; ]+)/', $value, $matches)) {
86 | $filename = $matches[1];
87 | break;
88 | }
89 | }
90 |
91 | if ($filename === false) {
92 | throw new NoOutputFileInResponse();
93 | }
94 |
95 | $file = fopen($dirPath . DIRECTORY_SEPARATOR . $filename, 'w');
96 | if ($file === false) {
97 | throw NativeFunctionErrored::createFromLastPhpError();
98 | }
99 |
100 | if (fwrite($file, $response->getBody()->getContents()) === false) {
101 | throw NativeFunctionErrored::createFromLastPhpError();
102 | }
103 |
104 | if (fclose($file) === false) {
105 | throw NativeFunctionErrored::createFromLastPhpError();
106 | }
107 |
108 | return $filename;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/HrtimeIndex.php:
--------------------------------------------------------------------------------
1 | baseUrl);
16 | }
17 |
18 | public function screenshot(): ChromiumScreenshot
19 | {
20 | return new ChromiumScreenshot($this->baseUrl);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Modules/ChromiumCookie.php:
--------------------------------------------------------------------------------
1 | */
23 | public function jsonSerialize(): array
24 | {
25 | $serialized = [
26 | 'name' => $this->name,
27 | 'value' => $this->value,
28 | 'domain' => $this->domain,
29 | ];
30 |
31 | if ($this->path !== null) {
32 | $serialized['path'] = $this->path;
33 | }
34 |
35 | if ($this->secure !== null) {
36 | $serialized['secure'] = $this->secure;
37 | }
38 |
39 | if ($this->httpOnly !== null) {
40 | $serialized['httpOnly'] = $this->httpOnly;
41 | }
42 |
43 | if ($this->sameSite !== null) {
44 | $serialized['sameSite'] = $this->sameSite;
45 | }
46 |
47 | return $serialized;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Modules/ChromiumMultipartFormDataModule.php:
--------------------------------------------------------------------------------
1 | formValue('omitBackground', true);
24 |
25 | return $this;
26 | }
27 |
28 | /**
29 | * Sets the duration (i.e., "1s", "2ms", etc.) to wait when loading an HTML
30 | * document before converting it to PDF.
31 | */
32 | public function waitDelay(string $delay): self
33 | {
34 | $this->formValue('waitDelay', $delay);
35 |
36 | return $this;
37 | }
38 |
39 | /**
40 | * Sets the JavaScript expression to wait before converting an HTML
41 | * document to PDF until it returns true.
42 | *
43 | * For instance: "window.status === 'ready'".
44 | */
45 | public function waitForExpression(string $expression): self
46 | {
47 | $this->formValue('waitForExpression', $expression);
48 |
49 | return $this;
50 | }
51 |
52 | /**
53 | * Forces Chromium to emulate the media type "print".
54 | */
55 | public function emulatePrintMediaType(): self
56 | {
57 | $this->formValue('emulatedMediaType', 'print');
58 |
59 | return $this;
60 | }
61 |
62 | /**
63 | * Forces Chromium to emulate the media type "screen".
64 | */
65 | public function emulateScreenMediaType(): self
66 | {
67 | $this->formValue('emulatedMediaType', 'screen');
68 |
69 | return $this;
70 | }
71 |
72 | /**
73 | * Cookies to store in the Chromium cookie jar.
74 | *
75 | * @param ChromiumCookie[] $cookies
76 | *
77 | * @throws NativeFunctionErrored
78 | */
79 | public function cookies(array $cookies): self
80 | {
81 | $json = json_encode($cookies);
82 | if ($json === false) {
83 | throw NativeFunctionErrored::createFromLastPhpError();
84 | }
85 |
86 | $this->formValue('cookies', $json);
87 |
88 | return $this;
89 | }
90 |
91 | /**
92 | * Overrides the default 'User-Agent' HTTP header.
93 | */
94 | public function userAgent(string $userAgent): self
95 | {
96 | $this->formValue('userAgent', $userAgent);
97 |
98 | return $this;
99 | }
100 |
101 | /**
102 | * Sets extra HTTP headers that Chromium will send when loading the HTML
103 | * document.
104 | *
105 | * @param array $headers
106 | *
107 | * @throws NativeFunctionErrored
108 | */
109 | public function extraHttpHeaders(array $headers): self
110 | {
111 | $json = json_encode($headers);
112 | if ($json === false) {
113 | throw NativeFunctionErrored::createFromLastPhpError();
114 | }
115 |
116 | $this->formValue('extraHttpHeaders', $json);
117 |
118 | return $this;
119 | }
120 |
121 | /**
122 | * Forces Gotenberg to return a 409 Conflict response if the HTTP status
123 | * code from the main page is not acceptable.
124 | *
125 | * @param int[] $statusCodes
126 | *
127 | * @throws NativeFunctionErrored
128 | */
129 | public function failOnHttpStatusCodes(array $statusCodes): self
130 | {
131 | $json = json_encode($statusCodes);
132 | if ($json === false) {
133 | throw NativeFunctionErrored::createFromLastPhpError();
134 | }
135 |
136 | $this->formValue('failOnHttpStatusCodes', $json);
137 |
138 | return $this;
139 | }
140 |
141 | /**
142 | * Forces Gotenberg to return a 409 Conflict response if the HTTP status
143 | * code from at least one resource is not acceptable.
144 | *
145 | * @param int[] $statusCodes
146 | *
147 | * @throws NativeFunctionErrored
148 | */
149 | public function failOnResourceHttpStatusCodes(array $statusCodes): self
150 | {
151 | $json = json_encode($statusCodes);
152 | if ($json === false) {
153 | throw NativeFunctionErrored::createFromLastPhpError();
154 | }
155 |
156 | $this->formValue('failOnResourceHttpStatusCodes', $json);
157 |
158 | return $this;
159 | }
160 |
161 | /**
162 | * Forces Gotenberg to return a 409 Conflict if Chromium fails to load at
163 | * least one resource.
164 | */
165 | public function failOnResourceLoadingFailed(): self
166 | {
167 | $this->formValue('failOnResourceLoadingFailed', true);
168 |
169 | return $this;
170 | }
171 |
172 | /**
173 | * Forces Gotenberg to return a 409 Conflict response if there are
174 | * exceptions in the Chromium console.
175 | */
176 | public function failOnConsoleExceptions(): self
177 | {
178 | $this->formValue('failOnConsoleExceptions', true);
179 |
180 | return $this;
181 | }
182 |
183 | /**
184 | * Specifies whether Chromium have to wait or not for its network to be
185 | * idle.
186 | */
187 | public function skipNetworkIdleEvent(bool $skip = true): self
188 | {
189 | $this->formValue('skipNetworkIdleEvent', $skip ?: '0');
190 |
191 | return $this;
192 | }
193 |
194 | /**
195 | * Sets the additional files, like images, fonts, stylesheets, and so on.
196 | */
197 | public function assets(Stream ...$assets): self
198 | {
199 | foreach ($assets as $asset) {
200 | $this->formFile($asset->getFilename(), $asset->getStream());
201 | }
202 |
203 | return $this;
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/Modules/ChromiumPdf.php:
--------------------------------------------------------------------------------
1 | formValue('singlePage', true);
24 |
25 | return $this;
26 | }
27 |
28 | /**
29 | * Overrides the default paper size, using various units such as 72pt,
30 | * 96px, 1in, 25.4mm, 2.54cm, or 6pc. The default unit is inches when
31 | * not specified.
32 | *
33 | * Examples of paper size (width x height):
34 | *
35 | * Letter - 8.5 x 11 (default)
36 | * Legal - 8.5 x 14
37 | * Tabloid - 11 x 17
38 | * Ledger - 17 x 11
39 | * A0 - 33.1 x 46.8
40 | * A1 - 23.4 x 33.1
41 | * A2 - 16.54 x 23.4
42 | * A3 - 11.7 x 16.54
43 | * A4 - 8.27 x 11.7
44 | * A5 - 5.83 x 8.27
45 | * A6 - 4.13 x 5.83
46 | */
47 | public function paperSize(float|string $width, float|string $height): self
48 | {
49 | $this->formValue('paperWidth', $width);
50 | $this->formValue('paperHeight', $height);
51 |
52 | return $this;
53 | }
54 |
55 | /**
56 | * Overrides the default margins (i.e., 0.39), using various units such as
57 | * 72pt, 96px, 1in, 25.4mm, 2.54cm, or 6pc. The default unit is inches when
58 | * not specified.
59 | */
60 | public function margins(float|string $top, float|string $bottom, float|string $left, float|string $right): self
61 | {
62 | $this->formValue('marginTop', $top);
63 | $this->formValue('marginBottom', $bottom);
64 | $this->formValue('marginLeft', $left);
65 | $this->formValue('marginRight', $right);
66 |
67 | return $this;
68 | }
69 |
70 | /**
71 | * Forces page size as defined by CSS.
72 | */
73 | public function preferCssPageSize(): self
74 | {
75 | $this->formValue('preferCssPageSize', true);
76 |
77 | return $this;
78 | }
79 |
80 | /**
81 | * Embeds the document outline into the PDF.
82 | */
83 | public function generateDocumentOutline(): self
84 | {
85 | $this->formValue('generateDocumentOutline', true);
86 |
87 | return $this;
88 | }
89 |
90 | /**
91 | * Generates tagged (accessible) PDF.
92 | */
93 | public function generateTaggedPdf(): self
94 | {
95 | $this->formValue('generateTaggedPdf', true);
96 |
97 | return $this;
98 | }
99 |
100 | /**
101 | * Prints the background graphics.
102 | */
103 | public function printBackground(): self
104 | {
105 | $this->formValue('printBackground', true);
106 |
107 | return $this;
108 | }
109 |
110 | /**
111 | * Sets the paper orientation to landscape.
112 | */
113 | public function landscape(): self
114 | {
115 | $this->formValue('landscape', true);
116 |
117 | return $this;
118 | }
119 |
120 | /**
121 | * Overrides the default scale of the page rendering (i.e., 1.0).
122 | */
123 | public function scale(float $scale): self
124 | {
125 | $this->formValue('scale', $scale);
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Set the page ranges to print, e.g., "1-5, 8, 11-13".
132 | * Empty means all pages.
133 | */
134 | public function nativePageRanges(string $ranges): self
135 | {
136 | $this->formValue('nativePageRanges', $ranges);
137 |
138 | return $this;
139 | }
140 |
141 | /**
142 | * Adds a header to each page.
143 | *
144 | * Note: it automatically sets the filename to "header.html", as required
145 | * by Gotenberg.
146 | */
147 | public function header(Stream $header): self
148 | {
149 | $this->formFile('header.html', $header->getStream());
150 |
151 | return $this;
152 | }
153 |
154 | /**
155 | * Adds a footer to each page.
156 | *
157 | * Note: it automatically sets the filename to "footer.html", as required
158 | * by Gotenberg.
159 | */
160 | public function footer(Stream $footer): self
161 | {
162 | $this->formFile('footer.html', $footer->getStream());
163 |
164 | return $this;
165 | }
166 |
167 | /**
168 | * Splits the resulting PDF.
169 | */
170 | public function split(SplitMode $mode): self
171 | {
172 | $this->formValue('splitMode', $mode->mode);
173 | $this->formValue('splitSpan', $mode->span);
174 | $this->formValue('splitUnify', $mode->unify ?: '0');
175 |
176 | return $this;
177 | }
178 |
179 | /**
180 | * Sets the PDF/A format of the resulting PDF.
181 | */
182 | public function pdfa(string $format): self
183 | {
184 | $this->formValue('pdfa', $format);
185 |
186 | return $this;
187 | }
188 |
189 | /**
190 | * Enables PDF for Universal Access for optimal accessibility.
191 | */
192 | public function pdfua(): self
193 | {
194 | $this->formValue('pdfua', true);
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Sets the metadata to write.
201 | *
202 | * @param array> $metadata
203 | *
204 | * @throws NativeFunctionErrored
205 | */
206 | public function metadata(array $metadata): self
207 | {
208 | $json = json_encode($metadata);
209 | if ($json === false) {
210 | throw NativeFunctionErrored::createFromLastPhpError();
211 | }
212 |
213 | $this->formValue('metadata', $json);
214 |
215 | return $this;
216 | }
217 |
218 | /**
219 | * Converts a target URL to PDF.
220 | *
221 | * @throws NativeFunctionErrored
222 | */
223 | public function url(string $url): RequestInterface
224 | {
225 | $this->formValue('url', $url);
226 | $this->endpoint = '/forms/chromium/convert/url';
227 |
228 | return $this->request();
229 | }
230 |
231 | /**
232 | * Converts an HTML document to PDF.
233 | *
234 | * Note: it automatically sets the index filename to "index.html", as
235 | * required by Gotenberg.
236 | */
237 | public function html(Stream|null $index): RequestInterface
238 | {
239 | if ($index !== null) {
240 | $this->formFile('index.html', $index->getStream());
241 | }
242 |
243 | $this->endpoint = '/forms/chromium/convert/html';
244 |
245 | return $this->request();
246 | }
247 |
248 | /**
249 | * Converts one or more markdown files to PDF.
250 | *
251 | * Note: it automatically sets the index filename to "index.html", as
252 | * required by Gotenberg.
253 | */
254 | public function markdown(Stream|null $index, Stream ...$markdowns): RequestInterface
255 | {
256 | if ($index !== null) {
257 | $this->formFile('index.html', $index->getStream());
258 | }
259 |
260 | foreach ($markdowns as $markdown) {
261 | $this->formFile($markdown->getFilename(), $markdown->getStream());
262 | }
263 |
264 | $this->endpoint = '/forms/chromium/convert/markdown';
265 |
266 | return $this->request();
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/src/Modules/ChromiumScreenshot.php:
--------------------------------------------------------------------------------
1 | formValue('width', $width);
21 |
22 | return $this;
23 | }
24 |
25 | /**
26 | * The device screen height in pixels.
27 | */
28 | public function height(int $height): self
29 | {
30 | $this->formValue('height', $height);
31 |
32 | return $this;
33 | }
34 |
35 | /**
36 | * Defines whether to clip the screenshot according to the device
37 | * dimensions.
38 | */
39 | public function clip(): self
40 | {
41 | $this->formValue('clip', true);
42 |
43 | return $this;
44 | }
45 |
46 | /**
47 | * PNG as image compression format.
48 | */
49 | public function png(): self
50 | {
51 | $this->formValue('format', 'png');
52 |
53 | return $this;
54 | }
55 |
56 | /**
57 | * JPEG as image compression format.
58 | */
59 | public function jpeg(): self
60 | {
61 | $this->formValue('format', 'jpeg');
62 |
63 | return $this;
64 | }
65 |
66 | /**
67 | * WEBP as image compression format.
68 | */
69 | public function webp(): self
70 | {
71 | $this->formValue('format', 'webp');
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * The compression quality from range 0 to 100 (jpeg only).
78 | */
79 | public function quality(int $quality): self
80 | {
81 | $this->formValue('quality', $quality);
82 |
83 | return $this;
84 | }
85 |
86 | /**
87 | * Defines whether to optimize image encoding for speed, not for resulting
88 | * size.
89 | */
90 | public function optimizeForSpeed(): self
91 | {
92 | $this->formValue('optimizeForSpeed', true);
93 |
94 | return $this;
95 | }
96 |
97 | /**
98 | * Captures a screenshot of a target URL.
99 | *
100 | * @throws NativeFunctionErrored
101 | */
102 | public function url(string $url): RequestInterface
103 | {
104 | $this->formValue('url', $url);
105 | $this->endpoint = '/forms/chromium/screenshot/url';
106 |
107 | return $this->request();
108 | }
109 |
110 | /**
111 | * Captures a screenshot of an HTML document.
112 | *
113 | * Note: it automatically sets the index filename to "index.html", as
114 | * required by Gotenberg.
115 | */
116 | public function html(Stream|null $index): RequestInterface
117 | {
118 | if ($index !== null) {
119 | $this->formFile('index.html', $index->getStream());
120 | }
121 |
122 | $this->endpoint = '/forms/chromium/screenshot/html';
123 |
124 | return $this->request();
125 | }
126 |
127 | /**
128 | * Captures a screenshot of one or more markdown files.
129 | *
130 | * Note: it automatically sets the index filename to "index.html", as
131 | * required by Gotenberg.
132 | */
133 | public function markdown(Stream|null $index, Stream ...$markdowns): RequestInterface
134 | {
135 | if ($index !== null) {
136 | $this->formFile('index.html', $index->getStream());
137 | }
138 |
139 | foreach ($markdowns as $markdown) {
140 | $this->formFile($markdown->getFilename(), $markdown->getStream());
141 | }
142 |
143 | $this->endpoint = '/forms/chromium/screenshot/markdown';
144 |
145 | return $this->request();
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/Modules/LibreOffice.php:
--------------------------------------------------------------------------------
1 | index = $index;
31 |
32 | return $this;
33 | }
34 |
35 | /**
36 | * Sets the password for opening the source file.
37 | */
38 | public function password(string $password): self
39 | {
40 | $this->formValue('password', $password);
41 |
42 | return $this;
43 | }
44 |
45 | /**
46 | * Sets the paper orientation to landscape.
47 | */
48 | public function landscape(): self
49 | {
50 | $this->formValue('landscape', true);
51 |
52 | return $this;
53 | }
54 |
55 | /**
56 | * Sets the page ranges to print, e.g., "1-4"'.
57 | * Empty means all pages.
58 | *
59 | * Note: the page ranges are applied to all files independently.
60 | */
61 | public function nativePageRanges(string $ranges): self
62 | {
63 | $this->formValue('nativePageRanges', $ranges);
64 |
65 | return $this;
66 | }
67 |
68 | /**
69 | * Specifies whether to update the indexes before conversion, keeping in
70 | * mind that doing so might result in missing links in the final PDF.
71 | */
72 | public function updateIndexes(bool $update = true): self
73 | {
74 | $this->formValue('updateIndexes', $update ?: '0');
75 |
76 | return $this;
77 | }
78 |
79 | /**
80 | * Specifies whether form fields are exported as widgets or only their fixed
81 | * print representation is exported.
82 | */
83 | public function exportFormFields(bool $export = true): self
84 | {
85 | $this->formValue('exportFormFields', $export ?: '0');
86 |
87 | return $this;
88 | }
89 |
90 | /**
91 | * Specifies whether multiple form fields exported are allowed to have the
92 | * same field name.
93 | */
94 | public function allowDuplicateFieldNames(): self
95 | {
96 | $this->formValue('allowDuplicateFieldNames', true);
97 |
98 | return $this;
99 | }
100 |
101 | /**
102 | * Specifies if bookmarks are exported to PDF.
103 | */
104 | public function exportBookmarks(bool $export = true): self
105 | {
106 | $this->formValue('exportBookmarks', $export ?: '0');
107 |
108 | return $this;
109 | }
110 |
111 | /**
112 | * Specifies that the bookmarks contained in the source LibreOffice file
113 | * should be exported to the PDF file as Named Destination.
114 | */
115 | public function exportBookmarksToPdfDestination(): self
116 | {
117 | $this->formValue('exportBookmarksToPdfDestination', true);
118 |
119 | return $this;
120 | }
121 |
122 | /**
123 | * Exports the placeholders fields visual markings only. The exported
124 | * placeholder is ineffective.
125 | */
126 | public function exportPlaceholders(): self
127 | {
128 | $this->formValue('exportPlaceholders', true);
129 |
130 | return $this;
131 | }
132 |
133 | /**
134 | * Specifies if notes are exported to PDF.
135 | */
136 | public function exportNotes(): self
137 | {
138 | $this->formValue('exportNotes', true);
139 |
140 | return $this;
141 | }
142 |
143 | /**
144 | * Specifies if notes pages are exported to PDF. Notes pages are available
145 | * in Impress documents only.
146 | */
147 | public function exportNotesPages(): self
148 | {
149 | $this->formValue('exportNotesPages', true);
150 |
151 | return $this;
152 | }
153 |
154 | /**
155 | * Specifies, if the form field exportNotesPages is set to true, if only
156 | * notes pages are exported to PDF.
157 | */
158 | public function exportOnlyNotesPages(): self
159 | {
160 | $this->formValue('exportOnlyNotesPages', true);
161 |
162 | return $this;
163 | }
164 |
165 | /**
166 | * Specifies if notes in margin are exported to PDF.
167 | */
168 | public function exportNotesInMargin(): self
169 | {
170 | $this->formValue('exportNotesInMargin', true);
171 |
172 | return $this;
173 | }
174 |
175 | /**
176 | * Specifies that the target documents with .od[tpgs] extension, will have
177 | * that extension changed to .pdf when the link is exported to PDF. The
178 | * source document remains untouched.
179 | */
180 | public function convertOooTargetToPdfTarget(): self
181 | {
182 | $this->formValue('convertOooTargetToPdfTarget', true);
183 |
184 | return $this;
185 | }
186 |
187 | /**
188 | * Specifies that the file system related hyperlinks (file:// protocol)
189 | * present in the document will be exported as relative to the source
190 | * document location.
191 | */
192 | public function exportLinksRelativeFsys(): self
193 | {
194 | $this->formValue('exportLinksRelativeFsys', true);
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Exports, for LibreOffice Impress, slides that are not included in slide
201 | * shows.
202 | */
203 | public function exportHiddenSlides(): self
204 | {
205 | $this->formValue('exportHiddenSlides', true);
206 |
207 | return $this;
208 | }
209 |
210 | /**
211 | * Specifies that automatically inserted empty pages are suppressed. This
212 | * option is active only if storing Writer documents.
213 | */
214 | public function skipEmptyPages(): self
215 | {
216 | $this->formValue('skipEmptyPages', true);
217 |
218 | return $this;
219 | }
220 |
221 | /**
222 | * Specifies that a stream is inserted to the PDF file which contains the
223 | * original document for archiving purposes.
224 | */
225 | public function addOriginalDocumentAsStream(): self
226 | {
227 | $this->formValue('addOriginalDocumentAsStream', true);
228 |
229 | return $this;
230 | }
231 |
232 | /**
233 | * Ignores each sheet’s paper size, print ranges and shown/hidden status
234 | * and puts every sheet (even hidden sheets) on exactly one page.
235 | */
236 | public function singlePageSheets(): self
237 | {
238 | $this->formValue('singlePageSheets', true);
239 |
240 | return $this;
241 | }
242 |
243 | /**
244 | * Specifies if images are exported to PDF using a lossless compression
245 | * format like PNG or compressed using the JPEG format.
246 | */
247 | public function losslessImageCompression(): self
248 | {
249 | $this->formValue('losslessImageCompression', true);
250 |
251 | return $this;
252 | }
253 |
254 | /**
255 | * Specifies the quality of the JPG export. A higher value produces a
256 | * higher-quality image and a larger file. Between 1 and 100.
257 | */
258 | public function quality(int $quality): self
259 | {
260 | $this->formValue('quality', $quality);
261 |
262 | return $this;
263 | }
264 |
265 | /**
266 | * Specifies if the resolution of each image is reduced to the resolution
267 | * specified by the form field maxImageResolution.
268 | * FIXME: parameter not used.
269 | */
270 | public function reduceImageResolution(bool $notUsedAnymore = true): self
271 | {
272 | $this->formValue('reduceImageResolution', true);
273 |
274 | return $this;
275 | }
276 |
277 | /**
278 | * If the form field reduceImageResolution is set to true, tells if all
279 | * images will be reduced to the given value in DPI. Possible values are:
280 | * 75, 150, 300, 600 and 1200.
281 | */
282 | public function maxImageResolution(int $dpi): self
283 | {
284 | $this->formValue('maxImageResolution', $dpi);
285 |
286 | return $this;
287 | }
288 |
289 | /**
290 | * Sets the PDF/A format of the resulting PDF.
291 | */
292 | public function pdfa(string $format): self
293 | {
294 | $this->formValue('pdfa', $format);
295 |
296 | return $this;
297 | }
298 |
299 | /**
300 | * Enables PDF for Universal Access for optimal accessibility.
301 | */
302 | public function pdfua(): self
303 | {
304 | $this->formValue('pdfua', true);
305 |
306 | return $this;
307 | }
308 |
309 | /**
310 | * Sets the metadata to write.
311 | *
312 | * @param array> $metadata
313 | *
314 | * @throws NativeFunctionErrored
315 | */
316 | public function metadata(array $metadata): self
317 | {
318 | $json = json_encode($metadata);
319 | if ($json === false) {
320 | throw NativeFunctionErrored::createFromLastPhpError();
321 | }
322 |
323 | $this->formValue('metadata', $json);
324 |
325 | return $this;
326 | }
327 |
328 | /**
329 | * Merges the resulting PDFs.
330 | */
331 | public function merge(): self
332 | {
333 | $this->merge = true;
334 | $this->formValue('merge', true);
335 |
336 | return $this;
337 | }
338 |
339 | /**
340 | * Splits the resulting PDFs.
341 | */
342 | public function split(SplitMode $mode): self
343 | {
344 | $this->formValue('splitMode', $mode->mode);
345 | $this->formValue('splitSpan', $mode->span);
346 | $this->formValue('splitUnify', $mode->unify ?: '0');
347 |
348 | return $this;
349 | }
350 |
351 | /**
352 | * Defines whether the resulting PDF should be flattened.
353 | */
354 | public function flatten(): self
355 | {
356 | $this->formValue('flatten', true);
357 |
358 | return $this;
359 | }
360 |
361 | /**
362 | * Converts the given document(s) to PDF(s). Gotenberg will return either
363 | * a unique PDF if you request a merge or a ZIP archive with the PDFs.
364 | *
365 | * Note: if you requested a merge, the merging order is determined by the
366 | * order of the arguments.
367 | */
368 | public function convert(Stream ...$files): RequestInterface
369 | {
370 | $index = $this->index ?? new HrtimeIndex();
371 | foreach ($files as $file) {
372 | $filename = $this->merge ? $index->create() . '_' . $file->getFilename() : $file->getFilename();
373 | $this->formFile($filename, $file->getStream());
374 | }
375 |
376 | $this->endpoint = '/forms/libreoffice/convert';
377 |
378 | return $this->request();
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/src/Modules/PdfEngines.php:
--------------------------------------------------------------------------------
1 | index = $index;
30 |
31 | return $this;
32 | }
33 |
34 | /**
35 | * Sets the PDF/A format of the resulting PDF.
36 | */
37 | public function pdfa(string $format): self
38 | {
39 | $this->formValue('pdfa', $format);
40 |
41 | return $this;
42 | }
43 |
44 | /**
45 | * Enables PDF for Universal Access for optimal accessibility.
46 | */
47 | public function pdfua(): self
48 | {
49 | $this->formValue('pdfua', true);
50 |
51 | return $this;
52 | }
53 |
54 | /**
55 | * Sets the metadata to write.
56 | *
57 | * @param array> $metadata
58 | *
59 | * @throws NativeFunctionErrored
60 | */
61 | public function metadata(array $metadata): self
62 | {
63 | $json = json_encode($metadata);
64 | if ($json === false) {
65 | throw NativeFunctionErrored::createFromLastPhpError();
66 | }
67 |
68 | $this->formValue('metadata', $json);
69 |
70 | return $this;
71 | }
72 |
73 | /**
74 | * Defines whether the resulting PDF should be flattened.
75 | * Prefer the flatten method if you only want to flatten one or more PDFs.
76 | */
77 | public function flattening(): self
78 | {
79 | $this->formValue('flatten', true);
80 |
81 | return $this;
82 | }
83 |
84 | /**
85 | * Merges PDFs into a unique PDF.
86 | *
87 | * Note: the merging order is determined by the order of the arguments.
88 | */
89 | public function merge(Stream ...$pdfs): RequestInterface
90 | {
91 | $index = $this->index ?? new HrtimeIndex();
92 |
93 | foreach ($pdfs as $pdf) {
94 | $this->formFile($index->create() . '_' . $pdf->getFilename(), $pdf->getStream());
95 | }
96 |
97 | $this->endpoint = '/forms/pdfengines/merge';
98 |
99 | return $this->request();
100 | }
101 |
102 | /**
103 | * Splits PDF(s).
104 | * Gotenberg will return the PDF or a ZIP archive with the PDFs.
105 | */
106 | public function split(SplitMode $mode, Stream ...$pdfs): RequestInterface
107 | {
108 | $this->formValue('splitMode', $mode->mode);
109 | $this->formValue('splitSpan', $mode->span);
110 | $this->formValue('splitUnify', $mode->unify ?: '0');
111 |
112 | foreach ($pdfs as $pdf) {
113 | $this->formFile($pdf->getFilename(), $pdf->getStream());
114 | }
115 |
116 | $this->endpoint = '/forms/pdfengines/split';
117 |
118 | return $this->request();
119 | }
120 |
121 | /**
122 | * Flatten PDF(s).
123 | * Gotenberg will return the PDF or a ZIP archive with the PDFs.
124 | */
125 | public function flatten(Stream ...$pdfs): RequestInterface
126 | {
127 | $this->formValue('flatten', true);
128 |
129 | foreach ($pdfs as $pdf) {
130 | $this->formFile($pdf->getFilename(), $pdf->getStream());
131 | }
132 |
133 | $this->endpoint = '/forms/pdfengines/flatten';
134 |
135 | return $this->request();
136 | }
137 |
138 | /**
139 | * Converts PDF(s) to a specific PDF/A format.
140 | * Gotenberg will return the PDF or a ZIP archive with the PDFs.
141 | */
142 | public function convert(string $pdfa, Stream ...$pdfs): RequestInterface
143 | {
144 | $this->pdfa($pdfa);
145 |
146 | foreach ($pdfs as $pdf) {
147 | $this->formFile($pdf->getFilename(), $pdf->getStream());
148 | }
149 |
150 | $this->endpoint = '/forms/pdfengines/convert';
151 |
152 | return $this->request();
153 | }
154 |
155 | /**
156 | * Retrieves the metadata of specified PDFs, returning a JSON formatted
157 | * response with the structure filename => metadata.
158 | */
159 | public function readMetadata(Stream ...$pdfs): RequestInterface
160 | {
161 | foreach ($pdfs as $pdf) {
162 | $this->formFile($pdf->getFilename(), $pdf->getStream());
163 | }
164 |
165 | $this->endpoint = '/forms/pdfengines/metadata/read';
166 |
167 | return $this->request();
168 | }
169 |
170 | /**
171 | * Allows writing specified metadata to one or more PDF.
172 | *
173 | * @param array> $metadata
174 | *
175 | * @throws NativeFunctionErrored
176 | */
177 | public function writeMetadata(array $metadata, Stream ...$pdfs): RequestInterface
178 | {
179 | $this->metadata($metadata);
180 |
181 | foreach ($pdfs as $pdf) {
182 | $this->formFile($pdf->getFilename(), $pdf->getStream());
183 | }
184 |
185 | $this->endpoint = '/forms/pdfengines/metadata/write';
186 |
187 | return $this->request();
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/MultipartFormDataModule.php:
--------------------------------------------------------------------------------
1 | > */
20 | private array $multipartFormData = [];
21 |
22 | /**
23 | * Overrides the default UUID output filename.
24 | * Note: Gotenberg adds the file extension automatically; you don't have to
25 | * set it.
26 | */
27 | public function outputFilename(string $filename): self
28 | {
29 | $this->headers['Gotenberg-Output-Filename'] = $filename;
30 |
31 | return $this;
32 | }
33 |
34 | /**
35 | * Sets the URLs to download files from.
36 | *
37 | * @param DownloadFrom[] $downloadFrom
38 | *
39 | * @throws NativeFunctionErrored
40 | */
41 | public function downloadFrom(array $downloadFrom): self
42 | {
43 | $json = json_encode($downloadFrom);
44 | if ($json === false) {
45 | throw NativeFunctionErrored::createFromLastPhpError();
46 | }
47 |
48 | $this->formValue('downloadFrom', $json);
49 |
50 | return $this;
51 | }
52 |
53 | /**
54 | * Sets the callback and error callback that Gotenberg will use to send
55 | * respectively the output file and the error response.
56 | */
57 | public function webhook(string $url, string $errorUrl): self
58 | {
59 | $this->headers['Gotenberg-Webhook-Url'] = $url;
60 | $this->headers['Gotenberg-Webhook-Error-Url'] = $errorUrl;
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * Overrides the default HTTP method that Gotenberg will use to call the
67 | * webhook.
68 | *
69 | * Either "POST", "PATCH", or "PUT" - default "POST".
70 | */
71 | public function webhookMethod(string $method): self
72 | {
73 | $this->headers['Gotenberg-Webhook-Method'] = $method;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * Overrides the default HTTP method that Gotenberg will use to call the
80 | * error webhook.
81 | *
82 | * Either "POST", "PATCH", or "PUT" - default "POST".
83 | */
84 | public function webhookErrorMethod(string $method): self
85 | {
86 | $this->headers['Gotenberg-Webhook-Error-Method'] = $method;
87 |
88 | return $this;
89 | }
90 |
91 | /**
92 | * Sets the extra HTTP headers that Gotenberg will send alongside the
93 | * request to the webhook and error webhook.
94 | *
95 | * @param array $headers
96 | *
97 | * @throws NativeFunctionErrored
98 | */
99 | public function webhookExtraHttpHeaders(array $headers): self
100 | {
101 | $json = json_encode($headers);
102 | if ($json === false) {
103 | throw NativeFunctionErrored::createFromLastPhpError();
104 | }
105 |
106 | $this->headers['Gotenberg-Webhook-Extra-Http-Headers'] = $json;
107 |
108 | return $this;
109 | }
110 |
111 | protected function formValue(string $name, mixed $value): self
112 | {
113 | $this->multipartFormData[] = [
114 | 'name' => $name,
115 | 'contents' => $value,
116 | ];
117 |
118 | return $this;
119 | }
120 |
121 | protected function formFile(string $filename, StreamInterface $stream): void
122 | {
123 | $this->multipartFormData[] = [
124 | 'name' => 'files',
125 | 'filename' => $filename,
126 | 'contents' => $stream,
127 | ];
128 | }
129 |
130 | protected function request(string $method = 'POST'): RequestInterface
131 | {
132 | $body = new MultipartStream($this->multipartFormData);
133 |
134 | $request = Psr17FactoryDiscovery::findRequestFactory()
135 | ->createRequest($method, $this->url . $this->endpoint)
136 | ->withHeader('Content-Type', 'multipart/form-data; boundary="' . $body->getBoundary() . '"')
137 | ->withBody($body);
138 |
139 | foreach ($this->headers as $key => $value) {
140 | $request = $request->withHeader($key, $value);
141 | }
142 |
143 | return $request;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/SplitMode.php:
--------------------------------------------------------------------------------
1 | filename;
47 | }
48 |
49 | public function getStream(): StreamInterface
50 | {
51 | return $this->stream;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------