Atomic Smash homepage splash

Create PDF Documents in WordPress with FPDF

Words by Anthony HartnellJanuary 11, 2018

In this tutorial, I will show you how to create a PDF document using the content from posts within WordPress. I’ll be using FPDF –  a free and open source PDF library written in PHP, and integrate it into a custom admin plugin to manage the code.

What is FPDF?

FPDF Library Website

FPDF is a lightweight PDF library written in PHP. It weighs in at less than 100Kb which is a fraction of the size of other libraries like mPDF (40Mb~). The number of features and functions are really good and the manual is very clear to follow. It’s also a powerful library that can be extended easily to offer only the functionality you need.

By combining it with WordPress a range of possibilities can be achieved, for example, creating a pdf document from image galleries and building custom reports. In fact, every bit of data inside WordPress can be sent to FPDF when needed.

Aim of this tutorial

In the following example I will produce a single PDF file taken from the content in the 5 most recent posts. I’ll start by setting up the folder structure for a plugin and adding the code to build a custom admin interface. This will give me a page from which to create the PDF.

First download FDPF from http://www.fpdf.org/. The latest version as of writing is 1.81. I’ve unzipped this in to a plugin folder called atomicsmash-pdf-tutorial and created two blank php files inside of this called atomicsmash-pdf-helper-functions.php and atomicsmash-pdf-tutorial.php.

My folder structure now looks like this:

Add the following code to  atomicsmash-pdf-tutorial.php:

<?php
/*
* Plugin Name: Atomic Smash FPDF Tutorial
* Description: A plugin created to demonstrate how to build a PDF document from WordPress posts.
* Version: 1.0
* Author: Anthony Hartnell
* Author URI: https://www.atomicsmash.co.uk/blog/author/anthony/
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

include( 'atomicsmash-pdf-helper-functions.php');
$pdf = new PDF_HTML();

if( isset($_POST['generate_posts_pdf'])){
    output_pdf();
}

add_action( 'admin_menu', 'as_fpdf_create_admin_menu' );
function as_fpdf_create_admin_menu() {
    $hook = add_submenu_page(
        'tools.php',
        'Atomic Smash PDF Generator',
        'Atomic Smash PDF Generator',
        'manage_options',
        'as-fdpf-tutorial',
        'as_fpdf_create_admin_page'
    );
}

function output_pdf() {
    $posts = get_posts( 'posts_per_page=5' );

    if( ! empty( $posts ) ) {
        global $pdf;
        $title_line_height = 10;
        $content_line_height = 8;

        $pdf->AddPage();
        $pdf->SetFont( 'Arial', '', 42 );
        $pdf->Write(20, 'Atomic Smash FPDF Tutorial');

        foreach( $posts as $post ) {

            $pdf->AddPage();
            $pdf->SetFont( 'Arial', '', 22 );
            $pdf->Write($title_line_height, $post->post_title);

            // Add a line break
            $pdf->Ln(15);

            // Image
            $page_width = $pdf->GetPageWidth() - 20;
            $max_image_width = $page_width;

            $image = get_the_post_thumbnail_url( $post->ID );
            if( ! empty( $image ) ) {
                $pdf->Image( $image, null, null, 100 );
            }
            
            // Post Content
            $pdf->Ln(10);
            $pdf->SetFont( 'Arial', '', 12 );
            $pdf->WriteHTML($post->post_content);
        }
    }

    $pdf->Output('D','atomic_smash_fpdf_tutorial.pdf');
    exit;
}


function as_fpdf_create_admin_page() {
?>
<div class="wrap">
    <h1>Atomic Smash - Wordpress PDF Generator</h1>
    <p>Click below to generate a pdf from the content inside all the Wordpress Posts. </p>
    <p>Each post will be on its own pdf page containing the post title and post content.</p>
	<form method="post" id="as-fdpf-form">
        <button class="button button-primary" type="submit" name="generate_posts_pdf" value="generate">Generate PDF from Wordpress Posts</button>
    </form>
</div>
<?php
}

After saving, go to the plugins page and you should see it listed:

After activating it you’ll now see a link to the plugin admin page in the Tools menu:

What is happening here?

On line 14 I’m including a php file which will contain some FPDF helper functions. By default, FPDF does not interpret HTML tags or external links so we have to extend the library with some extra code. This code contains a class called PDF_HTML which extends the standard FPDF class- you can see that being instantiated on line 15 and stored in the $pdf variable. The helper functions will be added at the end.

Triggering the 'output_pdf' function

On line 17 I’m checking if a $_POST request containing a key called generate_posts_pdf has a value that is set and not empty. This is being sent by the submit button in the admin page html on line 82. The value is only sent if the button is pressed so, effectively it’s a simple way of adding interaction to a plugin to kick off a function.

The output_pdf function uses get_posts and loops through each one to add content to the PDF with some simply named functions called AddPage and Write. The documentation is very well written and mostly self explanatory but outputting the post_content will not work without adding the helper functions.

Adding the helper functions

You might be wondering why the FPDF library can’t interpret HTML tags. I believe this is what makes the library so small to begin with. Their tutorials demonstrate that you are encouraged to extend the functionality of the library and add in the features where necessary. This makes it more customisable.

On Line 66 I’m utilising the WriteHTML function and this is included in the code that should go in the atomicsmash-pdf-helper-functions.php file.  The code I modified can be found here: http://www.fpdf.org/en/script/script42.php.

Enter the following code into atomicsmash-pdf-helper-functions.php:

<?php
include( 'fpdf181/fpdf.php');
/**
 * Modified from http://www.fpdf.org/en/script/script42.php
 */

//function hex2dec
//returns an associative array (keys: R,G,B) from
//a hex html code (e.g. #3FE5AA)
function hex2dec($color = "#000000"){
    $R = substr($color, 1, 2);
    $rouge = hexdec($R);
    $V = substr($color, 3, 2);
    $vert = hexdec($V);
    $B = substr($color, 5, 2);
    $bleu = hexdec($B);
    $tbl_color = array();
    $tbl_color['R']=$red;
    $tbl_color['G']=$green;
    $tbl_color['B']=$blue;
    return $tbl_color;
}

//conversion pixel -> millimeter at 72 dpi
function px2mm($px){
    return $px*25.4/72;
}

function txtentities($html){
    $trans = get_html_translation_table(HTML_ENTITIES);
    $trans = array_flip($trans);
    return strtr($html, $trans);
}
////////////////////////////////////
class PDF_HTML extends FPDF
{
//variables of html parser
protected $B;
protected $I;
protected $U;
protected $HREF;
protected $fontList;
protected $issetfont;
protected $issetcolor;

function __construct($orientation='P', $unit='mm', $format='A4')
{
    //Call parent constructor
    parent::__construct($orientation,$unit,$format);
    //Initialization
    $this->B=0;
    $this->I=0;
    $this->U=0;
    $this->HREF='';
    $this->fontlist=array('arial', 'times', 'courier', 'helvetica', 'symbol');
    $this->issetfont=false;
    $this->issetcolor=false;
}

function WriteHTML($html)
{
    //HTML parser
    $html=strip_tags($html,"<b><u><i><a><img><p><br><strong><em><font><tr><blockquote>");
    $html=str_replace("\n",' ',$html);
    $a=preg_split('/<(.*)>/U',$html,-1,PREG_SPLIT_DELIM_CAPTURE);
    foreach($a as $i=>$e)
    {
        if($i%2==0)
        {
            //Text
            if($this->HREF)
                $this->PutLink($this->HREF,$e);
            else
                $this->Write(5,stripslashes(txtentities($e)));
        }
        else
        {
            //Tag
            if($e[0]=='/')
                $this->CloseTag(strtoupper(substr($e,1)));
            else
            {
                //Extract attributes
                $a2=explode(' ',$e);
                $tag=strtoupper(array_shift($a2));
                $attr=array();
                foreach($a2 as $v)
                {
                    if(preg_match('/([^=]*)=["\']?([^"\']*)/',$v,$a3))
                        $attr[strtoupper($a3[1])]=$a3[2];
                }
                $this->OpenTag($tag,$attr);
            }
        }
    }
}

function OpenTag($tag, $attr)
{
    //Opening tag
    switch($tag){
        case 'STRONG':
            $this->SetStyle('B',true);
            break;
        case 'EM':
            $this->SetStyle('I',true);
            break;
        case 'B':
        case 'I':
        case 'U':
            $this->SetStyle($tag,true);
            break;
        case 'A':
            $this->HREF=$attr['HREF'];
            break;
        case 'IMG':
            if(isset($attr['SRC']) && (isset($attr['WIDTH']) || isset($attr['HEIGHT']))) {
                if(!isset($attr['WIDTH']))
                    $attr['WIDTH'] = 0;
                if(!isset($attr['HEIGHT']))
                    $attr['HEIGHT'] = 0;
                $this->Image($attr['SRC'], $this->GetX(), $this->GetY(), px2mm($attr['WIDTH']), px2mm($attr['HEIGHT']));
            }
            break;
        case 'TR':
        case 'BLOCKQUOTE':
        case 'BR':
            $this->Ln(5);
            break;
        case 'P':
            $this->Ln(10);
            break;
        case 'FONT':
            if (isset($attr['COLOR']) && $attr['COLOR']!='') {
                $colour=hex2dec($attr['COLOR']);
                $this->SetTextColor($colour['R'],$colour['G'],$colour['B']);
                $this->issetcolor=true;
            }
            if (isset($attr['FACE']) && in_array(strtolower($attr['FACE']), $this->fontlist)) {
                $this->SetFont(strtolower($attr['FACE']));
                $this->issetfont=true;
            }
            break;
    }
}

function CloseTag($tag)
{
    //Closing tag
    if($tag=='STRONG')
        $tag='B';
    if($tag=='EM')
        $tag='I';
    if($tag=='B' || $tag=='I' || $tag=='U')
        $this->SetStyle($tag,false);
    if($tag=='A')
        $this->HREF='';
    if($tag=='FONT'){
        if ($this->issetcolor==true) {
            $this->SetTextColor(0);
        }
        if ($this->issetfont) {
            $this->SetFont('arial');
            $this->issetfont=false;
        }
    }
}

function SetStyle($tag, $enable)
{
    //Modify style and select corresponding font
    $this->$tag+=($enable ? 1 : -1);
    $style='';
    foreach(array('B','I','U') as $s)
    {
        if($this->$s>0)
            $style.=$s;
    }
    $this->SetFont('',$style);
}

function PutLink($URL, $txt)
{
    //Put a hyperlink
    $this->SetTextColor(0,0,255);
    $this->SetStyle('U',true);
    $this->Write(5,$txt,$URL);
    $this->SetStyle('U',false);
    $this->SetTextColor(0);
}

}//end of class

The end result

After clicking Tools > Atomic Smash PDF Generator you should see the admin page like so:

Click on the button to download the PDF which should look similar to the image below:

If you try this out, please feel free to leave any comments or suggestions below.

Profile picture of Anthony Hartnell

Anthony HartnellDeveloper

Anthony works in the development team and enjoys creating plugins and integrating maps, libraries and other APIs into Wordpress.

Go back to top

Keep up to date with Atomic news