├── 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 | Gotenberg PHP Logo 3 |

Gotenberg PHP

4 |

A PHP client for interacting with Gotenberg

5 |

6 | Latest Version 7 | Total Downloads 8 | Monthly Downloads 9 | Continuous Integration 10 | https://codecov.io/gh/gotenberg/gotenberg 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 | --------------------------------------------------------------------------------