Laravel is one of the most popular web development frameworks. It allows you to create fast, modern, and scalable websites for almost every use case.
In this tutorial, you'll learn how to create PDF files using Laravel and mPDF. You'll create a website that shows the user a rich text editor where they can add whatever content they want, then transform that content into a PDF file.
You can find the full code for the tutorial at this GitHub repository.
Prerequisites
To follow along with this tutorial, you need to have PHP installed. As this tutorial uses Laravel 8 and mPDF v8 (the latest versions at the time of writing), you should have a 7.3 or 7.4 PHP version, as mPDF only supports up to version 7.4.
You also need to have Composer installed.
Project Setup
In this section, you'll set up a Laravel project. Open your terminal and run the following command:
composer create-project laravel/laravel laravel-pdf-tutorial
This will create the directory laravel-pdf-tutorial
with the Laravel installation inside.
Once it's done, change to that directory and install mPDF to be used later:
composer require mpdf/mpdf
Create Page
As mentioned, you'll create a web page that will show the user a text editor that they can use to enter any content they want. It'll also show some configurations for creating the PDF file.
Create the file resources/views/home.blade.php
with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>PDF Maker</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
<script>
tinymce.init({
selector: '.editor',
toolbar:
'styleselect | alignleft aligncenter alignright | bold italic underline strikethrough | image | bullist numlist | table',
plugins: 'image lists table',
automatic_uploads: true,
file_picker_types: 'image',
file_picker_callback: function (cb, value, meta) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.onchange = function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var base64 = reader.result.split(',')[1];
var blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
cb(blobInfo.blobUri(), { title: file.name });
};
reader.readAsDataURL(file);
};
input.click();
},
});
</script>
</head>
<body>
<div class="container my-4 mx-auto">
<form action="#" method="POST">
@csrf
<h1>PDF Maker</h1>
<h2>Configurations</h2>
<div class="mb-3">
<label for="name" class="form-label">PDF Name</label>
<input type="text" class="form-control" id="name" name="name" />
</div>
<div class="mb-3">
<label for="header" class="form-label">Header</label>
<textarea name="header" id="header" class="editor"></textarea>
</div>
<div class="mb-3">
<label for="footer" class="form-label">Footer</label>
<div id="footerHelp" class="form-text">You can use {PAGENO} to add page numbering to all pages</div>
<textarea name="footer" id="footer" class="editor"></textarea>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="show_toc" name="show_toc">
<label class="form-check-label" for="show_toc">
Show Table of Content?
</label>
</div>
<h2>Content</h2>
<textarea name="content" id="content" class="editor"></textarea>
<div class="text-center">
<button type="button" class="btn btn-primary">Create</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>
In this code, we show a form with configurations and a text editor for the content. We use Bootstrap for easy styling, and TinyMCE to easily insert an editor in our form. With TinyMCE, we also add an image picker based on this example from their documentation.
For the configurations, we show a field to enter the name of the PDF, a text editor to style the header, a text editor to style the footer, and a checkbox to enable or disable the table of content.
Next, in routes/web.php
change the route for the home page to the following:
Route::get('/', function () {
return view('home');
});
Now, run the server:
php artisan serve
If you open the website now at localhost:8000
(by default), you'll see the form we just created.
If you see a notice on the editors regarding an API key, this is because we're using the CDN without an API key. You can obtain an API key by signing up to Tiny Cloud, or by downloading and self-hosting the TinyMCE library.
Notice that for the footer, we add a note that to add a page number to all pages we can use the placeholder {PAGENO}
. This can be added to the header or footer.
Create PDF
In this section, you'll create the controller that will handle the form submission and create the PDF.
In your terminal, run the following command:
php artisan make:controller PdfController
Then, open the controller at app/Http/Controllers/PdfController.php
and place the following method inside the class:
public function createPdf (Request $request) {
//validate request
Validator::validate($request->all(), [
'name' => 'required|string|min:1',
'content' => 'required|string|min:1'
]);
//create PDF
$mpdf = new Mpdf();
$header = trim($request->get('header', ''));
$footer = trim($request->get('footer', ''));
if (strlen($header)) {
$mpdf->SetHTMLHeader($header);
}
if (strlen($footer)) {
$mpdf->SetHTMLFooter($footer);
}
if ($request->get('show_toc')) {
$mpdf->h2toc = array(
'H1' => 0,
'H2' => 1,
'H3' => 2,
'H4' => 3,
'H5' => 4,
'H6' => 5
);
$mpdf->TOCpagebreak();
}
//write content
$mpdf->WriteHTML($request->get('content'));
//return the PDF for download
return $mpdf->Output($request->get('name') . '.pdf', Destination::DOWNLOAD);
}
Make sure to also add the necessary imports at the beginning of the file:
use Illuminate\Support\Facades\Validator;
use Mpdf\Mpdf;
use Mpdf\Output\Destination;
First, you validate the request using Laravel's Validate Facade. You can learn more about Laravel's Validation in this tutorial.
After validating the request parameters, you create a new instance of Mpdf
. You can pass a lot of configurations to mPDF's constructor which allows you to set things like the font used or the text direction.
Next, you retrieve the values for Header and Footer entered by the user. If there are any entered, you set the Header with SetHTMLHeader
and Footer with SetHTMLFooter
. This will set the header and footer on all pages.
Then, you check if show_toc
was checked. If it was, you add a Table of Content at the beginning of the PDF using TOCpagebreak
. You also use the mPDF instance's h2toc
parameter to define the hierarchy that should be used in the table of content. This makes it easier for Mpdf
to generate the table of content from the PDF's content.
Next, you set the content of the PDF using WriteHTML
. You will write the content
entered by the user as is. Since TinyMCE sends the content to the server as HTML, the content should look exactly the same in the editor as it is in the PDF.
Finally, you return the PDF for download using the Output
method. This method receives as a first parameter the file name, and as a second parameter the return type. Its values can be I
for inline (default behavior), D
for download which will force download in the browser, F
for file which should be used when storing the created file, and S
for String Return which should be used when the PDF should be returned as a string.
In this case, you use D
which will create the PDF and trigger the download automatically.
Now that the controller's method is finished, add the new route in routes/web.php
:
Route::post('/create', [App\Http\Controllers\PdfController::class, 'createPdf']);
And change the action
attribute of the form in resources/views/home.blade.php
:
<form action="/create" method="POST">
That's all! Now, open the home page again and enter all the content you want. You can enter images, tables, lists, and more. Once you're done, click on Create, and the PDF will be generated and downloaded. Notice how the content is exactly similar to how it looks in the editor.
Bonus: Adding Stylesheets
mPDF allows you to add styling to your PDF document. You can pass it as a stylesheet as follows:
$stylesheet = "h1 { color: red; }";
$mpdf->WriteHTML($stylesheet,\Mpdf\HTMLParserMode::HEADER_CSS);
Conclusion
There are a lot of use cases where you would need to create a PDF file. In Larave, you can easily do that using mPDF. There are so many functionalities and things you can do with mPDF so be sure to check out their documentation.