How to generate PDFs in Laravel

This guide will showcase two different PDF libraries which are laravel-dompdf and laravel-pdf, the reason why two libraries are featured in this guide is because dompdf relies solely on PHP whereas laravel-pdf requires JavaScript and the Chrome browser.

I switched to dompdf because of issues setting up Chrome in a Ubuntu web server, in terms of the desktop PC there were no issues encountered generating the PDF as Chrome was already installed and in use as it’s the browser I use for web development.

Laravel wrapper for dompdf

There are a few PDF libraries specifically for PHP that have been around for years, one of the most popular ones is dompdf.

As a PHP developer I typically use Laravel for the majority of my PHP projects, thankfully there is a Laravel wrapper for dompdf called laravel-pdf.

dompdf-github.png

Required software

The most obvious requirement is having PHP installed, this library requires at least PHP 7.1 but you should be on at least version 8.1 according to the php.net supported versions page.

Open up the terminal and change the current directory to an existing Laravel project, if none are available then create a new Lar

After going on the dompdf website you can see the requirements to run it.

Here is a list of the required PHP extensions.

  • dom
  • mbstring

Use the PHP modules command to verify that the required extensions are installed.

php -m

How to add the library

You will require a Laravel project, use an existing one if you are integrating with an existing project, or create a new one via the below command.

composer create-project laravel/laravel test-pdf-app

Inside of the project directory

composer require barryvdh/laravel-dompdf

How to create a test PDF

Create a blade view to add HTML template code.

php artisan make:view pdf-view

Open the pdf-view.blade.php file located in the resources/view directory and add the following content.

<div>
    <!-- Output the content of the data variable -->
    {{ $data }}
</div>

Create a command which will be used to generate the test PDF.

php artisan make:command GeneratePdf

Open the GeneratePdf.php file inside of the app/Console/Commands directory and add in the following test code.

<?php

namespace App\Console\Commands;

use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Console\Command;

class GeneratePdf extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:generate-pdf';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generates a test PDF file';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $data = 'Lorem ipsum dolor sit amet. Et rerum placeat qui fuga consequuntur 
            est numquam nemo At ipsam esse ut voluptates nulla. Eos veritatis inventore 
            qui quia voluptates aut voluptates cupiditate non repudiandae voluptatum in 
            cupiditate recusandae rem obcaecati recusandae et tempore atque. Sit itaque 
            error ut officia facilis et praesentium totam aut voluptatem quasi ad maiores 
            perferendis. Ex veniam aspernatur ab mollitia repudiandae ea iusto blanditiis 
            vel possimus omnis rem veritatis vitae in voluptatum laborum non ipsam 
            doloremque.';

        $pdf = Pdf::loadView('pdf-view', ['data' => $data]);
        $pdf->save('hello-world.pdf');
    }
}

Run the command with the artisan command.

php artisan app:generate-pdf

You will notice the hello-world.pdf file is added to your project directory, open it to see the text added as expected.

dompdf-pdf-2.png

Laravel-PDF from Spatie

After searching for PDF libraries I discovered a PDF library provided by Spatie, a company specializing in developing Laravel software and courses.

The GitHub page shows more information about the library.

spatie-laravel-pdf.png

How to add the PDF library

Open up the terminal and change the current directory to an existing Laravel project, if none are available then create a new Laravel project and change the current directory to inside of the app.

composer create-project laravel/laravel example-app

Run the following to add the PDF library to the project.

composer require spatie/laravel-pdf

Puppeteer is required for this library to work, use node packager manager to install.

 npm -g i puppeteer

Create a test PDF

After adding the PDF library follow these steps so you can try out the PDF and see for yourself the PDF files that get generated.

To test out the PDF library I will create a Laravel command and view files using artisan.

Run the below command to create a view named “pdf-view”.

php artisan make:view pdf-view

Open the pdf-view.blade.php file located inside of the meshu-api/resources/views directory and add the following code.

<div>
    <!-- Output the content of the data variable -->
    {{ $data }}
</div>

Run the below line to create a Laravel command class named “generate-pdf”.

php artisan make:command generate-pdf

Open the GeneratePdf.php file located inside the app/Console/Commands directory and add the following code.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Spatie\LaravelPdf\Facades\Pdf;

class GeneratePdf extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:generate-pdf';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generates a test PDF file';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $data = 'Lorem ipsum dolor sit amet. Et rerum placeat qui fuga consequuntur 
            est numquam nemo At ipsam esse ut voluptates nulla. Eos veritatis inventore 
            qui quia voluptates aut voluptates cupiditate non repudiandae voluptatum in 
            cupiditate recusandae rem obcaecati recusandae et tempore atque. Sit itaque 
            error ut officia facilis et praesentium totam aut voluptatem quasi ad maiores 
            perferendis. Ex veniam aspernatur ab mollitia repudiandae ea iusto blanditiis 
            vel possimus omnis rem veritatis vitae in voluptatum laborum non ipsam 
            doloremque.';

        Pdf::view('pdf-view', ['data' => $data])
            ->format('a4')
            ->save('hello-world.pdf');
    }
}

Now run the Laravel Command using the following inside of the terminal.

php artisan app:generate-pdf

Check the Laravel project's main directory and see that a hello-world.pdf is added. Open it using your PDF application and you will see the contents of the text added.

pdf-file2.png

Potential errors and fixes

This section covers errors that I’ve encountered while using the PDF library for my Laravel project and the fixes I used to resolve the issues.

Puppeteer related error: No usable sandbox

When working with the PDF plugin I ran into an error when generating a PDF, the error is as follows.

Error: Failed to launch the browser process!
[810484:810484:0609/165908.560808:FATAL:zygote_host_impl_linux.cc(126)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/main/docs/linux/suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.

To resolve you need to access the BrowserShot object which interacts with Puppeteer and set it to no sandbox mode.

Here is an example of code showing the fix.

Pdf::view('pdf-view', $viewParams)
    ->withBrowsershot(fn ($browsershot) => $browsershot->noSandbox())
    ->save($filePath);

Vite manifest not found

This error occurred because I didn’t run the node package install and build command required to get Vite installed and set up.

Firstly run npm install command.

npm run install

Then run the build command and the error will not show up when using the PDF library.

npm run build

Cannot find module puppeteer

When trying to use the PDF I received the following error, the part that sticks out is that the puppeteer module cannot be found.

Error Output:

**================**


**node:internal/modules/cjs/loader:1186**


**const err = new Error(message);**


**^**


**Error: Cannot find module 'puppeteer'**


**Require stack:**

- **/Users/mesh/dev/meshpro-api/vendor/spatie/browsershot/bin/browser.cjs**

**at Module._resolveFilename (node:internal/modules/cjs/loader:1186:15)**


**at Module._load (node:internal/modules/cjs/loader:1012:27)**


**at Module.require (node:internal/modules/cjs/loader:1271:19)**

To resolve I installed the puppeteer module.

npm i --save-dev puppeteer

Then I ran the build command.

npm run build

Failed to launch the browser process!

When trying to use browsershot on a web server I received the following error.

Error Output:

Error: Failed to launch the browser process!
/home/ubuntu/.cache/puppeteer/chrome/linux-126.0.6478.182/chrome-linux64/chrome: 1: Syntax error: ")" unexpected

To resolve I ran the following commands.

Install the Chromium browser.

sudo apt install chromium-browser

Install the npm package puppeteer globally.

sudo npm install -g puppeteer --unsafe-perm=true --allow-root

Install the following libraries on the web server.

sudo apt-get install libasound2 libcairo2 libpango-1.0-0 libxkbcommon0 libgbm1 libxrandr2 libxfixes3 libxdamage1 libxcomposite1 libcups2 libatk-bridge2.0-0 libnss3

Use the setChromePath method to use the path to the installed chromium browser.

Pdf::view('pdf-view', $viewParams)
    ->withBrowsershot(fn ($browsershot) => $browsershot->noSandbox()->setChromePath('/snap/bin/chromium'))
    ->save($cvFilePath);

Reference: https://github.com/spatie/browsershot/discussions/681