About RockPdf - mPDF helper module

mPDF helper module to easily create PDF files and make debugging easier.

Category Development Tools
Modules that have more to do with assisting development of a site rather than managing its content.
Release StateStable
Should be safe for use in production environments. *
Authorbernhard
Module Version2.0.5
Class NameRockPdf
Compatibility3.0
Date AddedAugust 11, 2018
Last UpdatedJuly 15, 2020
Recommended ByNew recommendations may take up to 1 day to appear.

Details

RockPdf is a wrapper for the mpdf library. Mpdf, as you may already know, is a commonly used PHP library that turns HTML content into PDF, complete with (at least basic) CSS styling, embedded images, and so on. While you could include the mpdf library directly in your code, accessing it through a module has some benefits:

First of all you have access to an instance of mpdf anywhere in your template or module files simply by calling $modules->get('RockPdf') – no need to include any additional files or anything like that.
RockPdf makes debugging layout issues notably easier by embedding parameters passed to its functions as HTML comments into the generated markup, and also by giving you an easy method to fetch the generated markup as-is.
While mpdf by default includes a massive blob of fonts, with RockPdf you get only the ones you really need – and you can still add more if needed, just by dropping the font files into specific directory.
Perhaps the most notable benefit from using RockPdf instead of directly including mpdf is related to the first bullet point above: by using RockPdf you get to keep your template and module files clean, and the API usage is always consistent. As an added bonus you also don't have to worry about keeping the mpdf library manually up to date.

Instructions

This module's files should be placed in /site/modules/RockPdf/
How to install or uninstall modules

README

RockPdf Docs

Getting the mpdf instance

You can do anything that is possible with Mpdf using the mpdf instance:

$pdf = $modules->get('RockPdf');
$mpdf = $pdf->mpdf;

See the mpdf docs: https://mpdf.github.io

Helper shortcuts

Using mpdf directly can be fine, but this module ships with some helpers/shortcuts that make working with mpdf and processwire easier. One example is debugging. When using Mpdf debugging can be tedious because you might get some unwanted results in your pdf and you don't know what's causing this problem. Getting only the generated HTML is also not possible by default and you would need to implement some other output strategy like shown here: https://mpdf.github.io/getting-started/html-or-php.html;

My solution is simple: There are some custom proxy functions that forward your instructions to Mpdf and log this request as HTML comment. You can then output the generated HTML without generating the PDF (wich is quicker and a lot easier to debug - think of wrong filepaths for example). You can even use the tracy console to check your code:

$pdf = $modules->get('RockPdf');
$pdf->set('SetHeader', 'This is my header text');
$pdf->write('Hello World ' . date('H:i:s'));
$pdf->write('<!-- my custom comment -->');
d($pdf->html()); // output html in tracy console
d($pdf->save()); // generate pdf
$pdf->dump(); // dump pdf to iframe in tracy console

Output:

save.png save.png

Different output types

You can use these methods to output your pdf files:

  • save() to save your file to the file system
  • show() to directly show your file in the browser
  • download() to force the browser to download the pdf

Using fonts

By default MPdf ships with a lot of fonts making the module over 80MB large. I removed almost all of them and you can place the fonts you need in your sites assets folder /site/assets/RockPdf/fonts. See https://mpdf.github.io/fonts-languages/fonts-in-mpdf-7-x.html

  • Get any TTF font and copy it to site/assets/RockPdf/fonts
  • Import this font in your code:
// tracy console
$pdf = $modules->get('RockPdf');
$pdf->settings([
  'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [
    'test' => [
      'R' => 'Garuda.ttf',
      'I' => 'Garuda.ttf',
    ]
  ],
]);
$pdf->write('Hello World ' . date('H:i:s'));
$pdf->write('<p style="font-family: test;">Hello World ' . date('H:i:s') . '</p>');
d($pdf->save());

Using FontAwesome 5 with mPDF

  • Download a copy of fontawesome (https://fontawesome.com/download, eg Free for Web)
  • Copy the TTF file into your /site/assets/RockPdf/fonts/ folder
  • Add your font to your settings and start using icons in your PDFs
// tracy console
$pdf = $modules->get('RockPdf');
$pdf->settings([
  'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [
    "far" => [
      'R' => "fa-regular-400.ttf",
      'I' => "fa-regular-400.ttf",
    ],
  ],
]);
$icon = "<i style='font-family: far;'>&#xf118;</i> ";
$pdf->write($icon.'Hello World ' . date('H:i:s'));
d($pdf->save());

img

You'll notice that we used the unicode representation of the icon. You can find all the codes on the cheatsheet (https://fontawesome.com/cheatsheet) or on the details page of the icon: https://fontawesome.com/icons/smile?style=regular

Be careful to use the correct style (regular, solid, etc) and unicode! Special thx to jamesfairhurst

Using metadata to get the unicode

Too complicated? RockPdf comes with a helper so that you do not need to take care of all this and just use the regular fontawesome classes that you might already be familiar with! To make that work, just copy the icons.json file that is shipped with fontawesome in the metadata folder into the RockPdf assets folder /site/assets/RockPdf/fonts.

// tracy console
$pdf = $modules->get('RockPdf');
$pdf->settings([
  'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [
    "far" => [
      'R' => "fa-regular-400.ttf",
      'I' => "fa-regular-400.ttf",
    ],
  ],
]);
$pdf->write("<style>.far { font-family: far; color: blue; }</style>");
$icon = $pdf->icon('far fa-smile');
$pdf->write($icon.'Hello World ' . date('H:i:s'));
d($pdf->html()); // print content to console
$pdf->save(); // save file to file system

img img

Using this technique you can easily style your icons using CSS or even LESS (when using RockLESS).

Another example

Unfortunately duotone icons do not work (if you know how to make them work please drop me a line!). Also styling the icons is sometimes a bit tricky - some CSS selectors work while others don't. Using classes directly on the icon worked best for me:

$icons = $pdf->icon('fas fa-guitar red-2x')
  .$pdf->icon('far fa-guitar red-2x')
  .$pdf->icon('fal fa-guitar red-2x')
  .$pdf->icon('fad fa-guitar red-2x');
.fab { font-family: fab; }
.fad { font-family: fad; }
.fal { font-family: fal; }
.far { font-family: far; }
.fas { font-family: fas; }
.red-2x { font-size: 10mm; color: red; }

img

Setting a Background (using mpdf features)

Example implementation in a custom module:

/**
 * Add Background PDF
 */
public function addBackground($pdf) {
  $page = $this->pages->get("template=settings");
  $pdfs = $page->getUnformatted('calendarbackground'); // files field
  if(!$pdfs OR !$pdfs->count()) return; // no field or no file
  $pdf->mpdf->SetDocTemplate($pdfs->first()->filename);
}

Page margins

$pdf = $modules->get('RockPdf');
$pdf->settings([
  'margin_top' => 50,
]);
$pdf->write('hello world');
$pdf->save();

Or via CSS:

$pdf = $modules->get('RockPdf');
$pdf->write("<style>@page { margin: 0 }</style>");
$pdf->write('hello world');
$pdf->save();

img

Page margins for print with cropmarks

// thx to https://stackoverflow.com/a/50245034/6370411
$pdf = $modules->get('RockPdf');
$pdf->settings([
  'mode' => 'utf-8',
  'format' => [214, 301],
  'img_dpi' => 300,
]);
$pdf->write('
<style>
  @page {
    /* regular A4 paper size is 210x297 */
    size: 211mm 298mm; /* trying some weird format to make sure it works */
    marks: crop;
  }
</style>
Content
');
d($pdf->save());

img

img

You see that the Trim Box shows our custom values 211x298 whereas the bounding box would show the paper size (214x301).

Combine RockPdf and RockLESS

$pdf = $modules->get('RockPdf');
$less = $modules->get('RockLESS');
$style = "
@padding-s: 10pt;
.border { border: 1pt solid #afafaf; }
.hello { .border; color: blue; padding-top: @padding-s; }
.world { .border; color: red; padding-top: @padding-s * 2; }
";
$css = "\n".$less->parse($style);
$pdf->write("<style>$css</style>");
$pdf->write("<div class='hello'>hello</div>");
$pdf->write("<div class='world'>world</div>");
d($pdf->save());

img

img

This is the result of $pdf->html()

<style>
.border {
  border: 1pt solid #afafaf;
}
.hello {
  border: 1pt solid #afafaf;
  color: blue;
  padding-top: 10pt;
}
.world {
  border: 1pt solid #afafaf;
  color: red;
  padding-top: 20pt;
}
</style>
<div class='hello'>hello</div>
<div class='world'>world</div>

Real life example using RockPdf and RockLESS

// parts of RockPdfCalendar module

  public function init() {
    $this->w = $w = 420; // paper width in mm
    $this->h = $h = 297; // paper height in mm
    $this->b = $b = 2; // bleed in mm

    /** @var RockPdf $pdf */
    $pdf = $this->modules->get('RockPdf');
    $pdf->settings([
      'mode' => 'utf-8',
      'format' => [($w+2*$b), ($h+2*$b)],
      'dpi' => 300,
      'img_dpi' => 300,
    ]);
    $this->addBackground($pdf);
    $this->addStyles($pdf);

    $this->pdf = $pdf;
  }

  /**
   * Add Background PDF
   * @return void
   */
  public function addBackground($pdf) {
    $page = $this->pages->get("template=settings");
    $pdfs = $page->getUnformatted('calendarbackground');
    if(!$pdfs OR !$pdfs->count()) return; // no field or no file
    $pdf->set('SetDocTemplate', $pdfs->first()->filename);
  }

  /**
   * Add styles
   */
  public function addStyles($pdf) {
    /** @var RockLESS $less */
    $less = $this->modules->get('RockLESS');
    $less->vars = [
      'w' => $this->w."mm",
      'h' => $this->h."mm",
      'b' => $this->b."mm",
    ];
    $css = $less->getCSS(__DIR__ . "/style.less")->css;
    $pdf->write("<style>\n$css</style>");
  }

Then all you have to do is call $modules->get('RockPdfCalendar')->show() to render the pdf in the browser :)