To generate PDFs in Laravel, we can use the package barryvdh/laravel-dompdf. This package allows you to convert Blade View files into PDFs that can either be displayed directly in the browser or downloaded.
To demonstrate, we’ll generate a simple PDF invoice:
Import package
Import the barryvdh/laravel-dompdf into our Laravel project:
> composer require barryvdh/laravel-dompdf
Routes
/routes/web.php
:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\InvoiceController;
Route::get('/invoice', [InvoiceController::class, 'generate']);
Controller
/App/Http/Controllers/InvoiceController.php
:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
class InvoiceController extends Controller
{
public function generate(Request $request)
{
$data = [
'to' => 'Sam Example',
'subtotal' => '5.00',
'tax' => '.35',
'total' => '5.35'
];
$pdf = Pdf::loadView('invoice', $data);
# Option 1) Show the PDF in the browser
return $pdf->stream();
# Option 2) Download the PDF
// return $pdf->download('invoice.pdf');
}
}
View
/resources/views/invoice.blade.php
:
<!doctype html>
<html lang='en'>
<head>
<title>Invoice for {{ $to }}</title>
<meta charset='utf-8'>
{{-- Use an absolute path when specifying the CSS so it works in the PDF --}}
<link href='{{ public_path('css/invoice.css') }}' rel='stylesheet'>
</head>
<body>
<h1>Invoice</h1>
<img src='images/invoice.png' class='invoice-icon' alt='Invoice icon'>
<p><span class='label'>Billed to:</span> {{ $to }}</p>
<p><span class='label'>Subtotal:</span> ${{ $subtotal }}</p>
<p><span class='label'>Tax:</span> ${{ $tax }}</p>
<p><span class='label'>Total:</span> ${{ $total }}</p>
</body>
</html>
CSS
/public/css/invoice.css
:
.invoice-icon {
width:100px
}
.label {
font-weight:bold;
font-family:Arial, Helvetica, sans-serif;
}
Add a preview mode
When working with PDFs I like to add a “preview” mode I can use during development that lets me quickly load the View in the browser instead of generating the PDF. This saves time, especially if you’re making a lot of edits to your layout/design/content.
The preview mode is activated by adding the query string ?preview
to the URL, e.g. https://demo.com.test/invoice?preview
.
Here’s the updated controller with preview mode added:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
class InvoiceController extends Controller
{
public function generate(Request $request)
{
$data = [
'to' => 'Sam Example',
'subtotal' => '5.00',
'tax' => '.35',
'total' => '5.35'
];
# Optional: Append ?preview to the URL to preview the output directly in the browser instead of downloading the PDF
if($request->has('preview')) {
$data['css'] = 'css/invoice.css';
return view('invoice', $data);
} else {
$data['css'] = public_path('css/invoice.css');
}
$pdf = Pdf::loadView('invoice', $data);
# Option 1) Show the PDF in the browser
return $pdf->stream();
# Option 2) Download the PDF
// return $pdf->download('invoice.pdf');
}
}
Observe that in the above code we dynamically set the path of the CSS depending on whether we’re loading the invoice as a regular View (where we need a relative path) or as a PDF (where we need an absolute path). To accommodate this, update your View so the CSS links like this:
<link href='{{ $css }}' rel='stylesheet'>
Result: