---
url: /advanced/attachments.md
description: >-
Embed file attachments and XMP metadata in PDFs. Supports EU e-invoicing
standards like ZUGFeRD and Factur-X.
---
# Attachments & Metadata
WeasyPrint supports embedding file attachments and XMP metadata in generated PDFs.
## Attachments
You can attach files to a PDF by calling `addAttachment()` on the factory after preparing a source:
```php
$factory->prepareSource('
Invoice
')
->addAttachment('/path/to/terms.pdf')
->addAttachment('/path/to/receipt.png');
```
The method is fluent, so you can chain as many attachments as needed.
### Relationships
You can optionally specify a relationship type for each attachment, which describes how the attachment relates to the PDF:
```php
$factory->prepareSource('Invoice
')
->addAttachment('/path/to/invoice.xml', relationship: 'Data')
->addAttachment('/path/to/terms.pdf', relationship: 'Supplement');
```
This maps to the `--attachment-relationship` flag in the WeasyPrint CLI.
### Using a `Source` Object
You can also add attachments via a `Source` object before passing it to the factory:
```php
use WeasyPrint\Objects\Source;
$source = new Source('Invoice
');
$source->addAttachment('/path/to/terms.pdf');
$factory->prepareSource($source);
```
### File Validation
Attachment files are validated when the PDF is built. If a file does not exist, an `AttachmentNotFoundException` is thrown.
## XMP Metadata
XMP (Extensible Metadata Platform) metadata can be embedded in the PDF using `addXmpMetadata()`. This accepts a path to an RDF/XML file:
```php
$factory->prepareSource('Invoice
')
->addXmpMetadata('/path/to/metadata.xml');
```
Like attachments, the method is fluent and can be called multiple times:
```php
$factory->prepareSource('Invoice
')
->addXmpMetadata('/path/to/facturx.xml')
->addXmpMetadata('/path/to/additional-metadata.xml');
```
### EU E-Invoicing
XMP metadata is particularly useful for EU e-invoicing standards like ZUGFeRD and Factur-X, where a conforming PDF must include structured metadata alongside an XML invoice attachment:
```php
$factory->prepareSource($invoiceHtml)
->addAttachment('/path/to/factur-x.xml', relationship: 'Data')
->addXmpMetadata('/path/to/factur-x-metadata.xml');
```
---
---
url: /usage/output.md
description: >-
Build PDF output and stream, download, or save it. Supports inline display,
forced downloads, Flysystem, and Laravel Storage.
---
# Building the `Output`
Now that you have [prepared a source](/usage/source), you are ready to build the output and return it to your user or save it to disk (or do whatever you'd like to do with it).
To do this, you can call the `->build()` method on the factory, which will return an instance of `WeasyPrint\Objects\Output`:
```php
$output = $factory->build();
```
## Using the `Output`
The `Output` object makes the following methods available:
### `stream()`
This method creates a Symfony [`StreamedResponse`](https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response) that may be used to download or inline the PDF to the client (such as a browser).
```php
public function stream(
string $filename,
array $headers = [],
StreamMode $mode = StreamMode::INLINE,
): StreamedResponse
```
The `$headers` parameter allows you to set custom HTTP response headers. For example, you might add cache control headers or custom application headers.
The `$mode` parameter sets the `Content-Disposition` HTTP header, which controls how the browser handles the PDF:
| Mode | Value | Behavior |
| ------------------------------ | ------------ | ------------------------------------- |
| `StreamMode::INLINE` (default) | `inline` | Display the PDF in the browser |
| `StreamMode::DOWNLOAD` | `attachment` | Force the browser to download the PDF |
**Examples:**
```php
use Symfony\Component\HttpFoundation\StreamedResponse;
use WeasyPrint\WeasyPrintFactory;
public function generatePdf(): StreamedResponse
{
$factory = new WeasyPrintFactory()
->prepareSource('WeasyPrint rocks!
');
$output = $factory->build();
return $output->stream('weasyprint-rocks.pdf');
}
```
For more control, use `stream()` with custom headers or a different mode:
```php
use WeasyPrint\Enums\StreamMode;
// Force the browser to download the PDF instead of displaying it:
$response = $output->stream(
'weasyprint-rocks.pdf',
mode: StreamMode::DOWNLOAD,
);
// Add custom headers (while keeping default inline behavior):
$response = $output->stream(
'weasyprint-rocks.pdf',
headers: ['X-Custom-Header' => 'value'],
);
// Do both things at the same time:
$response = $output->stream(
'weasyprint-rocks.pdf',
headers: ['X-Custom-Header' => 'value'],
mode: StreamMode::DOWNLOAD,
);
```
::: tip Working with Streamed Responses
For more information on working with the `$response` from these examples, see the [Symfony StreamedResponse documentation](https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response).
:::
### `download()`
This is a shorthand for `stream()` using `StreamMode::DOWNLOAD`.
```php
public function download(
string $filename,
array $headers = [],
): StreamedResponse
```
**Examples:**
```php
// Force the browser to download the PDF:
$response = $output->download('weasyprint-rocks.pdf');
// With custom headers:
$response = $output->download(
'weasyprint-rocks.pdf',
headers: ['X-Custom-Header' => 'value'],
);
```
### `inline()`
This is a shorthand for `stream()` using `StreamMode::INLINE` (the default behavior of `stream()`).
```php
public function inline(
string $filename,
array $headers = [],
): StreamedResponse
```
**Examples:**
```php
// Display the PDF in the browser:
$response = $output->inline('weasyprint-rocks.pdf');
// With custom headers:
$response = $output->inline(
'weasyprint-rocks.pdf',
headers: ['X-Custom-Header' => 'value'],
);
```
### As a string
Beyond streaming the PDF to the browser, you can also get the underlying string data and do whatever you'd like with it.
#### `Stringable`
The `Output` object implements `\Stringable`, and so may be consumed as a string. This can be useful in cases where you want to pass it to a class, method or function that expects a string.
For example, you can write directly to disk:
```php
file_put_contents('weasyprint-rocks.pdf', $output);
```
When assigning to a variable, you can also cast it:
```php
$pdfData = (string) $output;
```
#### Flysystem + AWS
You can use Flysystem to store PDFs directly to AWS S3:
```php
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use League\Flysystem\Filesystem;
use Aws\S3\S3Client;
$s3Client = new S3Client([
'version' => 'latest',
'region' => 'us-east-1',
]);
$adapter = new AwsS3V3Adapter($s3Client, 'bucket-name');
$filesystem = new Filesystem($adapter);
$filesystem->write('path/to/weasyprint-rocks.pdf', $output);
```
To learn more about how to use Flysystem with AWS S3, see the [official documentation](https://flysystem.thephpleague.com/docs/adapter/aws-s3-v3/).
#### Laravel Storage
With Laravel, use the `Storage` facade:
```php
use Illuminate\Support\Facades\Storage;
Storage::put('path/to/weasyprint-rocks.pdf', $output);
```
To learn more about how to use Laravel’s File Storage features, see the [official documentation](https://laravel.com/docs/filesystem).
#### `getData()`
If you don't want to take advantage of the `Stringable` interface, then you can simply get the data directly:
```php
$pdfData = $output->getData();
```
## Skipping `build()`
The factory also exposes `stream()`, `download()`, `inline()`, and `getData()` directly, so you can skip the explicit `build()` call when you don't need to interact with the `Output` object:
```php
$factory = new WeasyPrintFactory();
return $factory
->prepareSource('WeasyPrint rocks!
')
->download('weasyprint-rocks.pdf');
```
These methods have the same signatures as their `Output` counterparts - they call `build()` internally and forward the result.
---
---
url: /frameworks/laravel/pdf-class.md
description: >-
Create reusable, class-based PDFs in Laravel with WeasyPrint for PHP.
Encapsulate source, filename, config, and headers in a single class.
---
# Class-based PDFs
The Laravel integration provides an abstract `PDF` class that lets you encapsulate PDF logic in dedicated, reusable classes. This is useful when a PDF has its own source, filename, and configuration that you want to keep together.
## Creating a PDF Class
Extend `WeasyPrint\Integration\Laravel\PDF` and implement the `source()` and `filename()` methods:
```php
use WeasyPrint\Integration\Laravel\PDF;
use Illuminate\Contracts\Support\Renderable;
class InvoicePdf extends PDF
{
public function __construct(
private Invoice $invoice,
) {}
public function source(): Renderable
{
return view('invoices.template', [
'invoice' => $this->invoice,
]);
}
public function filename(): string
{
return "invoice-{$this->invoice->number}.pdf";
}
}
```
The `source()` method accepts the same types as `prepareSource()` - a string, a `Renderable`, or a `Source` object.
## Returning from Controllers
The `PDF` class implements Laravel's `Responsable` interface, so you can return it directly from a controller:
```php
class InvoiceController
{
public function show(Invoice $invoice)
{
return new InvoicePdf($invoice);
}
}
```
By default, this displays the PDF inline in the browser.
## Stream Modes
To change the default stream mode, override the `defaultStreamMode()` method:
```php
use WeasyPrint\Enums\StreamMode;
class InvoicePdf extends PDF
{
// ...
public function defaultStreamMode(): StreamMode
{
return StreamMode::DOWNLOAD;
}
}
```
You can also call the output methods directly for explicit control:
```php
$pdf = new InvoicePdf($invoice);
return $pdf->download();
return $pdf->inline();
return $pdf->stream(StreamMode::DOWNLOAD);
```
## Custom Configuration
Override the `config()` method to customise configuration for a specific PDF class:
```php
use WeasyPrint\Objects\Config;
class InvoicePdf extends PDF
{
// ...
public function config(Config $config): void
{
$config->timeout = 120;
$config->optimizeImages = true;
}
}
```
This works like `tapConfig()` - you mutate specific properties and everything else retains its defaults.
## Custom Headers
Override the `headers()` method to add custom HTTP response headers:
```php
class InvoicePdf extends PDF
{
// ...
public function headers(): array
{
return [
'X-Invoice-Number' => $this->invoice->number,
];
}
}
```
---
---
url: /advanced/config.md
description: >-
Configure WeasyPrint for PHP - all available options including PDF variants,
versions, image optimisation, and more.
---
# Configuration
The factory comes with sensible defaults out of the box, so configuration is entirely optional. When you do need to customise behaviour, there are a few ways to do it.
## Passing Configuration
As covered in the [Factory](/usage/factory) docs, you can pass configuration when creating a new instance - either as a `Config` object or an array:
```php
use WeasyPrint\Objects\Config;
use WeasyPrint\WeasyPrintFactory;
// Using the Config class (recommended for type-safety and IDE hints):
$factory = new WeasyPrintFactory(
new Config(
timeout: 120,
optimizeImages: true,
)
);
// Using an array:
$factory = new WeasyPrintFactory([
'timeout' => 120,
'optimize_images' => true,
]);
```
## Changing Configuration
After creating a factory, you can change its configuration using `tapConfig()` or `setConfig()`.
### `tapConfig()`
Tapping lets you mutate specific properties on the existing `Config` object while leaving everything else untouched:
```php
use WeasyPrint\Objects\Config;
$factory->tapConfig(
static function (Config $config): void {
$config->binary = '/usr/local/bin/weasyprint';
$config->timeout = 120;
}
);
```
### `setConfig()`
To replace the configuration entirely, use `setConfig()` with a new `Config` object:
```php
use WeasyPrint\Objects\Config;
$factory->setConfig(new Config(
binary: '/usr/local/bin/weasyprint',
timeout: 120,
));
```
::: warning
Unlike `tapConfig()`, this replaces the entire configuration. Any values you don't explicitly set will fall back to the `Config` constructor defaults, not any previously configured values.
:::
## Available Options
### `binary`
* **Type:** `string|null` - **Default:** `null`
The path to the WeasyPrint binary. If WeasyPrint is available globally, the package will find and use it automatically. Otherwise, provide an absolute path.
### `cachePrefix`
* **Type:** `string` - **Default:** `'weasyprint_cache'`
The prefix used for temporary filenames.
### `timeout`
* **Type:** `int` - **Default:** `60`
The number of seconds to allow a conversion to run for.
### `inputEncoding`
* **Type:** `string` - **Default:** `'utf-8'`
Force the input character encoding. Must be a valid encoding supported by `mb_list_encodings()`.
### `presentationalHints`
* **Type:** `bool` - **Default:** `true`
Enable or disable HTML Presentational Hints.
### `mediaType`
* **Type:** `string|null` - **Default:** `null`
Set the media type to use for CSS @media. Defaults to `print` at binary level.
### `baseUrl`
* **Type:** `string|null` - **Default:** `null`
Set the base URL for relative URLs in the HTML input.
### `stylesheets`
* **Type:** `array` - **Default:** `[]`
Additional stylesheets to use alongside the HTML input. Each entry may be an absolute file path or a URL.
### `processEnvironment`
* **Type:** `array` - **Default:** `['LC_ALL' => 'en_US.UTF-8']`
Environment variables passed to Symfony Process when executing the binary.
### `pdfVariant`
* **Type:** `PDFVariant|string|null` - **Default:** `null`
Specify a PDF variant. You can pass the enum directly or use its string value:
```php
use WeasyPrint\Enums\PDFVariant;
new Config(pdfVariant: PDFVariant::PDF_A_2B);
new Config(pdfVariant: 'pdf/a-2b');
```
::: details Available variants
**PDF/A:** `PDF_A_1B`, `PDF_A_2B`, `PDF_A_3B`, `PDF_A_1A`, `PDF_A_2A`, `PDF_A_3A`, `PDF_A_2U`, `PDF_A_3U`, `PDF_A_4U`, `PDF_A_4E`, `PDF_A_4F`
**PDF/UA:** `PDF_UA_1`, `PDF_UA_2`
**PDF/X:** `PDF_X_1A`, `PDF_X_3`, `PDF_X_4`, `PDF_X_5G`
**Debug:** `DEBUG`
:::
### `pdfVersion`
* **Type:** `PDFVersion|string|null` - **Default:** `null`
Specify a PDF version. You can pass the enum directly or use its string value:
```php
use WeasyPrint\Enums\PDFVersion;
new Config(pdfVersion: PDFVersion::VERSION_2_0);
new Config(pdfVersion: '2.0');
```
::: details Available versions
`VERSION_1_4` (`1.4`), `VERSION_1_7` (`1.7`), `VERSION_2_0` (`2.0`)
:::
### `skipCompression`
* **Type:** `bool` - **Default:** `false`
Do not compress PDFs. Useful for debugging.
### `customMetadata`
* **Type:** `bool` - **Default:** `false`
Include custom HTML meta tags in PDF metadata.
### `srgb`
* **Type:** `bool` - **Default:** `false`
Include the sRGB color profile. When running against WeasyPrint 69 or newer, this uses `--output-intent srgb`.
### `outputIntent`
* **Type:** `string|null` - **Default:** `null`
Set the PDF output intent. This maps to WeasyPrint 69's `--output-intent` option and accepts values such as `srgb`, `device-cmyk`, or the CSS identifier of a `@color-profile` rule.
### `optimizeImages`
* **Type:** `bool` - **Default:** `false`
Optimize the size of embedded images with no quality loss.
### `fullFonts`
* **Type:** `bool` - **Default:** `false`
When possible, embed unmodified font files in the PDF.
### `hinting`
* **Type:** `bool` - **Default:** `false`
Keep hinting information in embedded font files.
### `dpi`
* **Type:** `int|null` - **Default:** `null`
Set the maximum resolution of images embedded in the PDF. Must be greater than 0 when set.
### `jpegQuality`
* **Type:** `int|null` - **Default:** `null`
Set the JPEG output quality, from 0 (worst) to 95 (best).
### `pdfForms`
* **Type:** `bool` - **Default:** `false`
Render PDF forms from HTML elements.
### `noHttpRedirects`
* **Type:** `bool` - **Default:** `false`
Disable following HTTP redirects.
### `failOnHttpErrors`
* **Type:** `bool` - **Default:** `false`
Abort the conversion on HTTP errors.
## Validation
The `Config` class validates certain options on construction:
* `dpi` must be greater than 0 when set.
* `jpegQuality` must be between 0 and 95 when set.
* `inputEncoding` must be a valid encoding supported by PHP's `mb_list_encodings()`.
Invalid values will throw an exception immediately, so configuration errors surface early.
---
---
url: /contributing.md
description: >-
How to contribute to WeasyPrint for PHP - development environment, tests,
formatting, and commit guidelines.
---
# Contributing
Contributions are always welcome! Feel free to [submit a pull request](https://github.com/mikerockett/weasyprint/pulls) to the `main` or current-release branch:
1. **Non-breaking** changes must target the `V.x` branch (where `V` is the latest major version). If accepted, the PR will also be merged into the applicable branches for other maintained versions.
2. **Breaking** changes must target the `main` branch. These will be released in a new major version and won't be backported.
Provide as much detail as possible in your pull request, unless it's a trivial change.
## Development Environment
This package uses Docker for local development. You don't need PHP, Composer, or WeasyPrint installed on your machine - everything runs inside the container.
::: tip
A [justfile](https://github.com/casey/just) is provided for convenience. If you don't have Just installed, you can substitute `just ` with the equivalent `docker compose run --rm wp `.
:::
To get started, make sure you have Docker installed and then build the image:
```shell
just build
```
Then install dependencies:
```shell
just composer install
```
You can run any Composer command via `just composer `.
## Tests
If your changes impact existing tests, update them. If you're adding new functionality:
1. **New feature:** Create a new test case covering all functionality.
2. **Existing feature improvement:** Add a test to the applicable test case.
Run tests before submitting your PR:
```shell
just test
```
## Formatting
This package uses PHP CS Fixer for code formatting. Format all your changes before committing:
```shell
just fix
```
## Commit Messages
Keep commit messages short and to the point - for example, "fix timeout handling when binary is unresponsive" or "add support for PDF/UA-2 variant". Long messages are strongly discouraged. Avoid generic messages like "fix bug" or "update code".
For bug reports or feature requests, [open an issue](https://github.com/mikerockett/weasyprint/issues).
---
---
url: /usage/source.md
description: >-
Prepare HTML sources for PDF generation using strings, URLs, Renderable
objects, or Source instances.
---
# Creating a `Source`
Now that you have a [Factory instance](/usage/factory), you can prepare a source.
Sources prepare and hold the HTML content that WeasyPrint will render into a PDF.
On your `$factory` instance, you can use the `prepareSource()` method, which accepts a single argument that can be one of the following types:
* **`string`** - HTML content or a URL to fetch
* **`Renderable`** - An object implementing the `Renderable` interface
* **`Source`** - A `WeasyPrint\Objects\Source` instance
Here's the method signature:
```php
public function prepareSource(string|Renderable|Source $source): self
```
When this method is called, it sets the source on the factory, [ready to be built](/usage/output), and returns the factory.
## HTML String
The simplest approach, pass HTML directly as a string:
```php
$factory->prepareSource('Invoice
Total: USD 100.00
');
```
For longer HTML content, use a [HEREDOC](https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc):
```php
$factory->prepareSource(<<Invoice
Total: USD 100.00
HTML);
```
## URL
You can also pass in a URL, which will be fetched and rendered into an HTML string.
```php
$factory->prepareSource('https://example.com/invoice/123');
```
## Renderable
You can also use a class that implements the `Renderable` contract from Laravel's [Support library](https://packagist.org/packages/illuminate/support), which is already a dependency of this package. In this case, the `render()` method will be called automatically.
```php
use Illuminate\Contracts\Support\Renderable;
class InvoiceTemplate implements Renderable
{
public function __construct(private array $data) {}
public function render(): string
{
$total = number_format($this->data['total'], 2);
return <<Invoice
Total: USD $total
HTML;
}
}
// Usage
$factory->prepareSource(new InvoiceTemplate(['total' => 100.0]));
```
Laravel Views implement `Renderable`, so you can pass them directly:
```php
$factory->prepareSource(
view('invoices.template', ['total' => 100.0])
);
```
## Source Object
For more control, use the `Source` class:
```php
use WeasyPrint\Objects\Source;
$source = new Source('Invoice
');
$factory->prepareSource($source);
```
The `Source` class also supports attachments (files to include in the PDF):
```php
$source = new Source('Invoice
');
$source->addAttachment('/path/to/file.pdf');
$factory->prepareSource($source);
```
Alternatively, add attachments after preparing the source:
```php
$factory->prepareSource('Invoice
')
->addAttachment('/path/to/file.pdf');
```
---
---
url: /advanced/errors.md
description: >-
All exceptions thrown by WeasyPrint for PHP, including configuration, source,
build, and output errors.
---
# Error Handling
The package throws specific exceptions for different failure scenarios. All exceptions extend `RuntimeException`.
## Configuration Errors
### `InvalidConfigValueException`
Thrown when a config value fails validation during construction or after calling `tapConfig()` / `setConfig()`.
```php
use WeasyPrint\Exceptions\InvalidConfigValueException;
```
This is thrown for:
* `dpi` set to 0 or a negative number
* `jpegQuality` outside the 0-95 range
* `inputEncoding` not supported by `mb_list_encodings()`
## Source Errors
### `SourceNotSetException`
Thrown when calling `build()` or `addAttachment()` without first preparing a source.
```php
use WeasyPrint\Exceptions\SourceNotSetException;
```
## Build Errors
### `BinaryNotFoundException`
Thrown when the WeasyPrint binary cannot be found or is not executable. This applies both when a custom path is configured via the `binary` config option and when the package attempts to locate it automatically.
```php
use WeasyPrint\Exceptions\BinaryNotFoundException;
```
### `UnsupportedVersionException`
Thrown at the start of a build if the installed WeasyPrint binary version does not satisfy the package's version constraint.
```php
use WeasyPrint\Exceptions\UnsupportedVersionException;
```
### `AttachmentNotFoundException`
Thrown during a build if an attachment file path does not exist on disk.
```php
use WeasyPrint\Exceptions\AttachmentNotFoundException;
```
### `TemporaryFileException`
Thrown when the package fails to write the HTML source to a temporary file for processing.
```php
use WeasyPrint\Exceptions\TemporaryFileException;
```
## Output Errors
### `MissingOutputFileException`
Thrown when the WeasyPrint binary completes but no output file is found at the expected path.
```php
use WeasyPrint\Exceptions\MissingOutputFileException;
```
### `OutputReadFailedException`
Thrown when the output file exists but cannot be read into memory.
```php
use WeasyPrint\Exceptions\OutputReadFailedException;
```
---
---
url: /frameworks.md
description: >-
Framework integrations for WeasyPrint for PHP (previously WeasyPrint for
Laravel). Now framework-agnostic with optional Laravel support.
---
# Framework Integrations
Prior to v11, this package was called **WeasyPrint for Laravel** and required Laravel as a dependency. It has since been renamed to **WeasyPrint for PHP** and rebuilt to be fully framework-agnostic - Laravel is no longer required, though first-class support for it remains.
The core package works anywhere PHP runs. Framework integrations are optional extras that provide tighter, more idiomatic bindings for specific frameworks.
## Supported Frameworks
### Laravel
The package ships with a built-in Laravel integration, including automatic service container binding, configuration publishing, and a class-based PDF generation API.
Laravel Integration
### Tempest
We are considering adding first-class support for [Tempest](https://tempestphp.com) in a future release. If this is something you'd like to see, feel free to [open an issue](https://github.com/mikerockett/weasyprint/issues) to share your interest.
---
---
url: /usage/get-started.md
description: >-
Get started with WeasyPrint for PHP (previously WeasyPrint for Laravel).
Install the package and generate your first PDF in minutes.
---
# Getting Started
Getting up and running with WeasyPrint for PHP is straightforward. Within minutes, you'll be able to use WeasyPrint in your PHP projects to generate beautiful PDFs.
## Check Version Compatibility
Before diving in, please review the version support table, which outlines the compatibility between different versions of WeasyPrint and the package. Typically, it's recommended to ensure you're using the latest versions of both WeasyPrint and the package.
**[Supported Versions →](/versions)**
## Installation
First make sure the appropriate version of WeasyPrint is installed on your system:
WeasyPrint Installation Docs ↗
Then, install the package with Composer:
```shell
composer require rockett/weasyprint
```
If you're using Laravel, see the [Laravel integration guide](/frameworks/laravel/) for additional steps (optional).
---
---
url: /frameworks/laravel.md
description: >-
Laravel integration for WeasyPrint for PHP (previously WeasyPrint for Laravel)
- service container binding, config publishing, facade, and class-based PDFs.
---
# Laravel Integration
The package ships with a built-in Laravel integration that provides automatic service container binding, publishable configuration, a facade, and a class-based PDF generation API.
## Setup
After [installing the package](/usage/get-started), the service provider is auto-discovered by Laravel. No additional registration is needed.
### Publishing Configuration
To customise the default configuration, publish the config file:
```shell
php artisan vendor:publish --tag=weasyprint.config
```
This creates a `config/weasyprint.php` file in your application. All options can be set via environment variables - see the [Configuration](/advanced/config#available-options) reference for available options.
## Using the Factory
The service provider binds `WeasyPrintFactory` to the `WeasyPrint` contract as a scoped singleton, meaning a single instance is reused throughout a request lifecycle (including with Laravel Octane).
### Dependency Injection
As covered in the [Factory](/usage/factory) docs, inject the contract into your classes:
```php
use WeasyPrint\Contracts\WeasyPrint;
class InvoiceController
{
public function download(WeasyPrint $weasyprint)
{
return $weasyprint
->prepareSource(view('invoices.template', ['total' => 100.0]))
->build()
->download('invoice.pdf');
}
}
```
### Facade
The `WeasyPrint` facade provides static access to the factory:
```php
use WeasyPrint\Integration\Laravel\WeasyPrint;
$output = WeasyPrint::prepareSource('Invoice
')
->build();
return $output->download('invoice.pdf');
```
## Class-based PDFs
For a more structured approach, the Laravel integration also provides an abstract `PDF` class that lets you encapsulate source, filename, configuration, and headers in a single, reusable class.
Learn about class-based PDFs
---
---
url: /advanced/testing.md
description: >-
Test your PDF generation with WeasyPrint::fake(), a built-in test double for
Laravel, or mock the contract in any framework.
---
# Testing
The package provides a built-in fake for Laravel and implements a contract interface for easy mocking in any framework.
## Laravel: `WeasyPrint::fake()`
The Laravel facade provides a `fake()` method that swaps the container binding with a test double. The fake records all calls without invoking the WeasyPrint binary and provides fluent assertion methods.
```php
use WeasyPrint\Integration\Laravel\WeasyPrint;
it('generates an invoice PDF', function () {
$fake = WeasyPrint::fake();
$response = $this->get('/invoices/1/pdf');
$response->assertOk();
$fake->assertSourcePrepared()
->assertDownloaded('invoice.pdf');
});
```
### Available Assertions
All assertion methods are chainable.
#### `assertBuilt()`
Assert that `build()` was called. Optionally pass a count:
```php
$fake->assertBuilt();
$fake->assertBuilt(times: 2);
```
#### `assertNotBuilt()` / `assertNothingBuilt()`
Assert that `build()` was never called:
```php
$fake->assertNotBuilt();
```
#### `assertSourcePrepared()`
Assert that `prepareSource()` was called. Optionally pass a callback to inspect the source:
```php
$fake->assertSourcePrepared();
$fake->assertSourcePrepared(function ($source) {
expect($source)->toBeInstanceOf(Source::class);
});
```
#### `assertDownloaded()`
Assert that `download()` was called. Optionally assert the filename:
```php
$fake->assertDownloaded();
$fake->assertDownloaded('invoice.pdf');
```
#### `assertInlined()`
Assert that `inline()` was called. Optionally assert the filename:
```php
$fake->assertInlined();
$fake->assertInlined('invoice.pdf');
```
#### `assertStreamed()`
Assert that `stream()` was called. Optionally assert the filename and/or mode:
```php
use WeasyPrint\Enums\StreamMode;
$fake->assertStreamed();
$fake->assertStreamed('invoice.pdf');
$fake->assertStreamed('invoice.pdf', mode: StreamMode::DOWNLOAD);
```
#### `assertAttachmentAdded()`
Assert that `addAttachment()` was called. Optionally assert the path:
```php
$fake->assertAttachmentAdded();
$fake->assertAttachmentAdded('/path/to/terms.pdf');
```
#### `assertXmpMetadataAdded()`
Assert that `addXmpMetadata()` was called. Optionally assert the path:
```php
$fake->assertXmpMetadataAdded();
$fake->assertXmpMetadataAdded('/path/to/metadata.xml');
```
### Fake Return Values
The fake returns sensible defaults so your code can run through its full path:
* `build()` returns an `Output` with fake PDF data
* `getData()` returns `'%PDF-1.4 fake'`
* `stream()`, `download()`, `inline()` return a `StreamedResponse` with correct headers
* `getWeasyPrintVersion()` returns `'68.0'`
## Without a Framework
In a framework-agnostic setup, code against the `WeasyPrint\Contracts\WeasyPrint` interface and provide a mock in your tests:
```php
use WeasyPrint\Contracts\WeasyPrint;
class InvoiceService
{
public function __construct(
private WeasyPrint $weasyprint,
) {}
public function generate(string $html): string
{
return $this->weasyprint
->prepareSource($html)
->getData();
}
}
```
Then in your test:
```php
$mock = Mockery::mock(WeasyPrint::class);
$mock->shouldReceive('prepareSource')
->with('Invoice
')
->once()
->andReturnSelf();
$mock->shouldReceive('getData')
->once()
->andReturn('fake-pdf-data');
$service = new InvoiceService($mock);
$result = $service->generate('Invoice
');
expect($result)->toBe('fake-pdf-data');
```
You can also use the `FakeWeasyPrint` class directly without the facade:
```php
use WeasyPrint\Testing\FakeWeasyPrint;
$fake = new FakeWeasyPrint();
$service = new InvoiceService($fake);
$service->generate('Invoice
');
$fake->assertSourcePrepared()
->assertBuilt();
```
## Integration Testing
To verify the full pipeline including the WeasyPrint binary, use the factory directly. Make sure WeasyPrint is installed in your CI environment:
```php
use WeasyPrint\WeasyPrintFactory;
it('produces valid PDF output', function () {
$data = (new WeasyPrintFactory())
->prepareSource('Test
')
->getData();
expect($data)->toStartWith('%PDF-');
});
```
---
---
url: /usage/factory.md
description: >-
Learn how to create and configure a WeasyPrintFactory instance for PDF
generation, with support for dependency injection.
---
# The Factory
The `WeasyPrintFactory` is the core class for generating PDFs. It serves as the main entry point for all PDF generation operations.
## Instantiating a Factory
Create a new instance by directly instantiating the `WeasyPrintFactory` class:
```php
use WeasyPrint\WeasyPrintFactory;
$factory = new WeasyPrintFactory();
```
You can optionally pass configuration using the `Config` class for type-safety and clear option hints:
```php
use WeasyPrint\Objects\Config;
$factory = new WeasyPrintFactory(
new Config(
timeout: 60,
logOutput: false
)
);
```
Alternatively, you can pass an array, which is useful when injecting configuration from a repository (like Laravel's `config()` helper):
```php
$factory = new WeasyPrintFactory([
'timeout' => 60,
'log_output' => false,
]);
```
::: tip
The `Config` class is recommended as it provides type-safety and IDE autocomplete for available options.
:::
How to configure the factory
## Using in a Class
Once you have a factory instance, you can inject it into your services and use it to prepare sources and generate PDFs:
```php
factory = new WeasyPrintFactory();
}
public function generateDocument(string $documentId): void
{
// Use the factory for PDF generation
}
}
```
## Dependency Injection
For frameworks that support Dependency Injection or Inversion of Control (IoC) – where the framework automatically manages creating and passing objects to your classes – use the `WeasyPrint\Contracts\WeasyPrint` interface instead of the concrete class:
```php
singleton(
WeasyPrint::class,
fn () => new WeasyPrintFactory()
);
```
Check your framework's documentation for how to register singleton or shared services.
## Checking the WeasyPrint Version
You can check the installed WeasyPrint binary version using `getWeasyPrintVersion()`:
```php
$version = $factory->getWeasyPrintVersion();
// e.g. "68.0"
```
This can be useful for debugging or verifying your environment is set up correctly.
---
---
url: /versions.md
description: >-
Version compatibility table for WeasyPrint for PHP (previously WeasyPrint for
Laravel), including supported WeasyPrint, Laravel, and PHP versions.
---
# Versions & Compatibility
This package aims to support the latest version of WeasyPrint at the bare minimum. It also supports older versions of WeasyPrint, provided their CLI APIs are **identical**.
Since WeasyPrint makes CLI API changes only in new major versions, this package releases a new major version each time to reflect the drift.
If WeasyPrint has consecutive releases without CLI API changes, this package supports all of them under a single version.
For detailed information about changes and upgrade paths:
* [Changelog](https://github.com/mikerockett/weasyprint/blob/main/CHANGELOG.md) – Documents all changes between versions
* [Upgrade Guide](https://github.com/mikerockett/weasyprint/blob/main/UPGRADING.md) – Documents all upgrade paths
## Supported Versions
**Current** means the latest maintained version. Versions in **Maintenance** only get security updates and bug fixes.
| Version | Status | WeasyPrint Version | Laravel \* | PHP | Branch |
| -------- | ----------- | -------------------------- | ---------------- | ---- | ----------------------------------------------------------- |
| **11.x** | **Current** | 67, 68, 69 | 12.x, 13.x | 8.3+ | [11.x](https://github.com/mikerockett/weasyprint/tree/11.x) |
| **10.x** | Maintenance | 63, 64, 65, 66, 67\*\*, 68 | 10.x, 11.x, 12.x | 8.2+ | [10.x](https://github.com/mikerockett/weasyprint/tree/10.x) |
::: warning \* Important considerations about Laravel/Illuminate
Before v11, the package was **WeasyPrint for Laravel** and required Laravel as a dependency. It now runs independently without any framework requirements, though a Laravel binding is still available for those who want it.
The package still requires `illuminate/support` (tied to the versions listed above), which creates a versioning constraint for Laravel users, but doesn't affect framework-agnostic setups unless they use `illuminate/support` themselves.
We may introduce integrations for other frameworks like Tempest in the future.
:::
::: info \*\* WeasyPrint 67 support in 10.x
While support for WeasyPrint 67 was added in v10, the new CLI flags it introduced were not implemented until v11 of the package was released.
:::
## Unsupported Versions
We typically maintain up to two versions at a time, or just the current version if maintenance has aged out and there are no new releases in the pipeline (ex: if WeasyPrint development is paused or delayed for an extended period of time).
The versions listed below are **no longer supported** and **should not be used for new projects**. Existing projects using unsupported versions should upgrade to a supported version.
| Version | WeasyPrint Version | Laravel | PHP |
| ------- | ------------------ | ------------------- | ---- |
| `9.x` | 61, 62 | 10.x, 11.x | 8.2+ |
| `8.1.x` | 59, 60, 61 | 10.x, 11.x | 8.1+ |
| `8.0.x` | 59, 60 | 10.x | 8.1+ |
| `7.x` | Unchecked | 10.x | 8.1+ |
| `6.x` | Unchecked | 8.47 | 8.0+ |
| `5.x` | Unchecked | 8.0 | 8.0+ |
| `4.x` | Unchecked | 7.x, 8.x, 9.x, 10.x | 7.3+ |
::: warning Documentation for unsupported versions is not available on this site.
Some older versions may have their own documentation in their READMEs, which are accessible via the applicable version branches on GitHub.
:::