Create PDF Report
Introduction
As part of the Questionnaire Maker plugin it is necessary to
create a pdf report. The best tool to do this appears to be
TCPDF, which appears to be part of at least some Linux
distributions as php-tcpdf
. It is not included in WordPress.
Implementation
As both the plugin and TCPDF are hosted on GitHub, the best way to implement the plugin, and TCPDF if it is not available on the web server, is to clone the repositories and use symbolic links to provide the plugin and the library in the right places. The same method could be used to implement WordPress.
user@server:~ $ git clone https://github.com/billthefarmer/questionnaire.git
user@server:~ $ cd /var/www/html/wordpress/wp-content/plugins/
user@server:/var/www/html/wordpress/wp-content/plugins $ ln -s ~/questionnaire/wordpress questionnaire_maker
user@server:/var/www/html/wordpress/wp-content/plugins $ cd ~
user@server:~ $ git clone https://github.com/tecnickcom/TCPDF.git tcpdf
user@server:~ $ cd questionnaire/wordpress
user@server:~/questionnaire/wordpress $ ln -s ~/tcpdf
The plugin includes code to load TCPDF if it is present and show a
message if it isn’t. Using the path tcpdf/tcpdf.php
will pick it up
either from /usr/share/php
, if installed, or from the symbolic link.
// Include TCPDF, if present
$tcpdf_present = include_once 'tcpdf/tcpdf.php';
After more work on this project I needed another library which makes
use of composer for dependencies. TCPDF doesn’t have any, but you
can create a composer.json
in the plugin and load TCPDF.
user@server:~/questionnaire/wordpress $ php composer.phar require tecnickcom/tcpdf
Using composer, the code to load libraries changes to pick them up from the vendor folder.
// Include composer libraries, if present
$vendor_present = include_once 'vendor/autoload.php'
Displaying errors
To avoid the dreaded WordPress White Screen of Death (WSOD), you can temporarily put a couple of lines at the top of the plugin to display PHP errors.
error_reporting(E_ALL);
ini_set('display_errors', 1);
Creating the document
The code to create a pdf document is fairly straightforward. The examples given always seem to specify everything, but by making note of the defaults, that isn’t necessary.
// Create document
$pdf = new TCPDF();
$pdf->setPageUnit('pt');
// Remove default header/footer
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$margin = 72; // 1"
$pageWidth = $pdf->getPageWidth();
$pageHeight = $pdf->getPageHeight();
$textWidth = $pageWidth - ($margin * 2);
// Set margins
$pdf->SetMargins($margin, $margin, $margin);
$pdf->SetAutoPageBreak(true, $margin);
TCPDF doesn’t seem to include a function to move the insertion point
down a line, you need to nest three functions to get the right result.
The functions to add text make use of the MultiCell()
function. Also
the SetFont()
function is used to set the font type.
// New line
function new_text_line($pdf)
{
$pdf->Ln($pdf->getCellHeight($pdf->getFontSize()));
};
// Add text
function add_text_object($pdf, $text, $forename = '', $lastname = '')
{
if (!empty($text->type))
$pdf->SetFont('', $text->type);
else
$pdf->SetFont('', '');
if (!empty($text->size))
$pdf->SetFontSize($text->size);
if (!empty($text->color))
$pdf->SetTextColor($text->color);
if (!empty($text->y))
$pdf->SetY($text->y);
$subst = str_replace(['~forename~', '~lastname~'],
[$forename, $lastname], $text->text);
$pdf->MultiCell(0, 0, $subst, 0, 'L');
};
// Add report entry
function add_entry($pdf, $entry, $value)
{
$desc = $entry->desc;
$type = $entry->$value->type;
$text = $entry->$value->text;
$image = $entry->$value->image;
$y = $pdf->GetY();
$pdf->Image($path . $image, $margin, $y, $textWidth, 0,
'png', '', 'N');
$pdf->MultiCell(0, 0, $desc, 0, 'L');
new_text_line($pdf);
$pdf->SetFont('', 'B');
$pdf->MultiCell(0, 0, $type, 0, 'L');
new_text_line($pdf);
$pdf->SetFont('', '');
$pdf->MultiCell(0, 0, $text, 0, 'L');
new_text_line($pdf);
};
To display images requires a set of rules to place the image on the page. If the width is empty or zero, text width, else width. If the height is empty or zero, height in proportion to width, else height. If x is empty or zero, left margin, if negative, right margin, else x. If y is empty or zero, top margin, negative, bottom margin, else y.
// Add image
function add_image_object($pdf, $image, $margin, $textWidth,
$pageHeight, $pageWidth, $path)
{
$width = (!empty($image->width))? $image->width: $textWidth;
$height = (!empty($image->height))? $image->height: 0;
$x = (!empty($image->x))? ($image->x < 0)?
$pageWidth - $margin - $width: $image->x: $margin;
$y = (!empty($image->y))? ($image->y < 0)?
$pageHeight - $margin - $height: $image->y: $margin;
$type = (!empty($image->type))? $image->type: '';
$link = (!empty($image->link))? $image->link: '';
$pdf->Image($path . $image->src, $x, $y, $width, $height,
$type, $link);
};
The report starts with a preamble.
// Preamble
foreach ($pages as $page)
{
$pdf->AddPage();
// Text
foreach ($page->text as $text)
add_text_object($pdf, $text, $forename, $lastname);
// Images
foreach ($page->images as $image)
add_image_object($pdf, $image, $margin, $textWidth,
$pageHeight, $pageWidth, $path);
}
Then the report body. Because each section starts with an image, start a new page if near the bottom.
// Report
$pdf->AddPage();
if ($B)
add_entry($pdf, $answers->B, $B, $margin, $textWidth, $path);
if ($C)
add_entry($pdf, $answers->C, $C, $margin, $textWidth, $path);
if ($pdf->GetY() >= $pageHeight - ($margin * 3))
$pdf->AddPage();
// ...
Then the last page.
$pdf->AddPage();
// Last page text
foreach ($last->text as $text)
add_text_object($pdf, $text);
// Last page images
foreach ($last->images as $image)
add_image_object($pdf, $image, $margin, $textWidth,
$pageHeight, $pageWidth, $path);
Output the document, and return the path to it.
// Output document
$pdf->Output($path . 'report/' . $filename, 'F');
// Return file uri
return plugins_url('report/' . $filename, __FILE__);
Creating an email
To send the report, create an email and send it.
// Set fields
$to = "$username <$usermail>";
$from_email = $data->from_email;
$from_name = $data->from_name;
$from = "$from_name <$from_email>";
$subject = str_replace('~forename~', $forename, $data->subject);
$message = str_replace('~forename~', $forename, $data->message);
$content = "Content-Type: text/html";
$headers = ["From: $from",
$content];
// Get attachment path
$path = plugin_dir_path(__FILE__);
$attachments = $path . "report/" . $filename;
// Send mail
wp_mail($to, $subject, $message, $headers,
$attachments);
See Also
- Questionnaire Maker
- Whatever-o-meter revisited
- Whatever-o-meter
- Migrating this blog from Wordpress
- SlimStat