Rendering HTML to PDF in Laravel and Docker

You want to generate PDF files from HTML and CSS but don't want to limit yourself to some few CSS features that for example MPDF can process? Well, I'm happy to tell you that Chrome can do just that. πŸŽ‰ (And it works not only in PHP)

tl;dr

docker-compose.yml
services:
  # ...config of your application here...
  pdfrenderer:
    image: timoschwarzer/headless-pdf-renderer
    security_opt:
      - seccomp=./chromium.json

Drop the chromium.json seccomp profile next to your docker-compose.yml. You should now be able to perform a POST request against http://pdfrenderer:8082/render with html set to the HTML markup you want to render and get back a PDF file.

What you need

Starting the service

I made a simple API service that takes HTML and renders it to PDF using Chrome's print-to-PDF feature.

Grab the seccomp profile

We need to enable some system features that are otherwise restricted for Chrome to be able to use the sandbox. (We don't want to run it without a sandbox…)

To do that, we grab the according seccomp profile from here and put it in our project directory.

See Nicolas Portmann's article for more in-depth information about this.

Using standalone Docker

You can now start the service with the following docker command:

docker run \
  --security-opt seccomp=./chromium.json \
  -p 8082:8082 \
  timoschwarzer/headless-pdf-renderer

Now make a POST request against http://localhost:8082/render with the html request field set to your HTML markup to render it to PDF.

Using Docker Compose

The usage with Docker Compose works similar to using standalone Docker. The only difference is that you can ditch the port forward and just use the service name as host, as it will be resolved by Docker automatically:

docker-compose.yml
services:
  # ...config of your application here...
  pdfrenderer:
    image: timoschwarzer/headless-pdf-renderer
    security_opt:
      - seccomp=./chromium.json

Send aPOST request to http://pdfrenderer:8082/render with the html request field set to your HTML markup.

Rendering HTML to PDF in Laravel

Once you have the service running, you can use Laravel's HTTP Client API to generate PDF files from HTML:

$response = Http::post('http://pdfrenderer:8082/render', [
    'html' => view('invoice')->render(), // Render a Blade view
]);

$pdf = $response->body();

// Save the PDF file in storage...
Storage::put('pdfs', $pdf);

// ...or download it from a Controller
return response()->streamDownload(function () use (&$pdf) {
    echo $pdf;
}, 'My PDF.pdf', [
    'Content-Type' => 'application/pdf',
]);

Make sure you bundle all CSS inside your Blade template as the API currently does not support uploading multiple files.

See also

Timo's Blog