{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Dynamically generated PDF attachments for Freeform submissions
Posted on Nov 24, 2021 by Piotr Pogorzelski

Dynamically generated PDF attachments for Freeform submissions

How to attach dynamically generated PDF file to Freeform email submission in Craft CMS
Table of contents:
  • Introduction
  • Attaching functionality to the event
  • Genrating PDF with mPDF library
  • Dynamic PDF attachment with other plugins
  • Using other PDF generation libraries

Introduction #

In one of my recent projects, client needed contact form which would send confirmation message to person submitting form. This message was supposed to have pdf attachment generated from submitted data.

Here's what's needed to build such functionality:

  • First, we need the contact form. I decided to use advanced form builder plugin Freeform.
  • Right before the form is sent, we need to grab submission data.
  • Using this data, we need to dynamically generate PDF file.
  • Finally, we need to attach generated PDF file to submission and send it.

Because we will be working with emails, I recommend using mailtrap for testing.

Attaching functionality to the event #

First things first - creating the contact form. This is actually described pretty extensively in Freeform docs, so I will skip that part.

Our code will live in PHP module. Every Craft install starts with empty module - we just need to enable it in config/app.php. You can find more info about modules in Enhancing a Craft CMS 3 Website with a Custom Module article on nystudio blog.

We will run our custom code within Freeform EVENT_BEFORE_SEND event - it will allow us to grab message contents right before it is sent.

Place this code before module class code starts:

use Solspace\Freeform\Services\MailerService;
use Solspace\Freeform\Events\Mailer\SendEmailEvent;
use yii\base\Event;

Place this code in init() method of module class:

Event::on(
  MailerService::class,
  MailerService::EVENT_BEFORE_SEND,
  function (SendEmailEvent $event) {

    $formHandle = 'someForm';
    if($event->getForm()->getHandle() != $formHandle){
        return false;
    }
});        

Note how we check for form handle (someForm in our case), to verify if we are dealing with our specific form. Its important to perform such checks to prevent any unexpected bahaviour.

Genrating PDF with mPDF library #

To generate PDF file, we will use mpdf library. Install it with composer:

composer require mpdf/mpdf

We will use Twig templates to render HTML which will be used to generate our PDF document.

$variables = [
    'fields' => $event->getFieldValues()
];
$html = Craft::$app->getView()->renderTemplate('_emails/pdf_template.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);
$htmlHeader = Craft::$app->getView()->renderTemplate('_emails/pdf_header.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);
$htmlFooter = Craft::$app->getView()->renderTemplate('_emails/pdf_footer.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);

This code will look for _emails/pdf_template, _emails/header.twig, _emails/footer.twig in your templates directory. Separate files are used for main document body (which can be many pages long), footer and header (which are repeated for each page). getFieldValues() Freeform method is used to pass submission data into template context, as fields array containing form field values. You can also use other Freeform methods and properties to pass various submission and form data to Twig.

Here's a very simple example Twig template used to render PDF body:

Sender first name: {{fields.firstNameHandle}} <br>
Sender surname: {{fields.surnameHandle}} <br>

You can display page number in footer template by using {PAGENO} keyword. Note - this is not a Twig variable, but something mPdf specific.

Current page: {PAGENO}

Keep in mind that HTML tags and CSS styles that are accepted by mPDF are kinda restricted. For example, you cannot use flexbox. On the other hand, there are some custom HTML tags used by this library. For example, to create two column layout, you can use custom tag.

Once we render HTML markup, we can finally feed it into mPDF.

$pdf = new \Mpdf\Mpdf([
    'margin_top' => 30,
    'margin_left' => 15,
    'margin_right' => 15,
    'margin_bottom' => 30,
]);            

$pdf->SetHTMLHeader($htmlHeader);
$pdf->SetHTMLFooter($htmlFooter);
$pdf->WriteHTML($html);
$pdf = $pdf->Output('', \Mpdf\Output\Destination::STRING_RETURN);

This code created PDF file, applied some margins to it and injected HTML content into document. Now, we need to attach our file to form submission.

$message = $event->getMessage();
$fileName = 'attachment.pdf';

$message->attachContent($pdf, [
    'fileName' => $fileName,
    'contentType' => 'application/pdf',
]);

That's it - now our form email will have PDF attachment. Here's whole module code to copy and paste into your project:

Event::on(
  MailerService::class,
  MailerService::EVENT_BEFORE_SEND,
  function (SendEmailEvent $event) {

    // get specific form
    $formHandle = 'someForm';
    if($event->getForm()->getHandle() != $formHandle){
        return false;
    }

    // render pdf HTML
    $variables = [
        'fields' => $event->getFieldValues()
    ];
    $html = Craft::$app->getView()->renderTemplate('_emails/pdf_template.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);
    $htmlHeader = Craft::$app->getView()->renderTemplate('_emails/pdf_header.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);
    $htmlFooter = Craft::$app->getView()->renderTemplate('_emails/pdf_footer.twig', $variables, Craft::$app->view::TEMPLATE_MODE_SITE);

    // create pdf file
    $pdf = new \Mpdf\Mpdf([
        'margin_top' => 30,
        'margin_left' => 15,
        'margin_right' => 15,
        'margin_bottom' => 30,
    ]);            

    $pdf->SetHTMLHeader($htmlHeader);
    $pdf->SetHTMLFooter($htmlFooter);
    $pdf->WriteHTML($html);
    $pdf = $pdf->Output('', \Mpdf\Output\Destination::STRING_RETURN);

    // add pdf file to submission email
    $message = $event->getMessage();
    $fileName = 'attachment.pdf';

    $message->attachContent($pdf, [
        'fileName' => $fileName,
        'contentType' => 'application/pdf',
    ]);
    
}); 

Dynamic PDF attachment with other plugins #

If you want, you can easily adapt this code to work witth other contact from plugins, like Express form, Formie or even Contact form. You just need to use specific event emitted by that plugin and some method to add attachment to message that is being sent.

Using other PDF generation libraries #

If mPDF is causing you problems for some reason, you can try other libraries. For example - dompdf, which is actually used by Craft Commerce to generate PDF invoices.


If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Articles on blog:
  • User account management with Craft CMS
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


User account management with Craft CMS

Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski