PDF in WordPress using FPDF : Custom PDF Generator

1527 views
PDF_WordPress_FPDF_PDF_Generator_ipdata_code

Why Generate PDFs Inside WordPress?

PDF generation is one of those features that sounds optional β€” until your client absolutely needs it. Whether you`re building:

  • Invoice generators for WooCommerce stores
  • Certificate makers for LMS/course platforms
  • Downloadable reports from custom post types or ACF fields
  • Appointment summaries from booking plugins
  • Custom proposal PDFs tied to form submissions

you will eventually need to produce PDFs directly from WordPress without relying on expensive plugins or third-party services.

The good news: you don`t need a $200/year plugin to do this. PHP has battle-tested, free PDF libraries β€” and FPDF is the most lightweight and WordPress-friendly of them all.

This guide covers everything from zero to a fully working, downloadable PDF system inside WordPress β€” including plugin structure, shortcodes, dynamic data from the database, full-width table rows, Unicode support, and a polished download button.

 

What is FPDF – and Why Choose It Over the Alternatives?

FPDF (Free PDF) is a pure-PHP class that lets you generate PDF files without any external dependencies. It`s been around since 1999, is actively maintained, and works on virtually every PHP hosting environment.

Here is how it compares to the other popular PHP PDF libraries:

Feature FPDF TCPDF mPDF Dompdf
HTML → PDF ❌ No βœ… Partial βœ… Yes βœ… Yes
Pure PHP (no extensions) βœ… Yes βœ… Yes βœ… Yes βœ… Yes
File size 🟒 Tiny (~150KB) πŸ”΄ Large (~20MB) 🟑 Medium (~30MB) 🟑 Medium
Learning curve 🟒 Easy 🟑 Moderate 🟑 Moderate 🟒 Easy
Custom layouts/tables βœ… Excellent βœ… Excellent βœ… Good ⚠️ Limited
WordPress-friendly βœ… Best fit βœ… Works βœ… Works βœ… Works
Unicode support ⚠️ Needs FPDF-MB βœ… Built-in βœ… Built-in βœ… Built-in

FPDF is the right choice when:

  • You need lightweight, fast PDF output
  • You`re building structured documents (invoices, tables, reports)
  • You want full layout control without the overhead of HTML parsing
  • Your hosting is shared/budget (no composer, limited disk space)

If your primary need is converting styled HTML pages into PDF, consider mPDF or Dompdf instead.

 

Project Overview β€” What We are Building

By the end of this guide, you`ll have:

  1. A custom WordPress plugin that loads FPDF cleanly
  2. A reusable PDF generation function that pulls dynamic data from WordPress
  3. A shortcode [generate_pdf] you can drop anywhere
  4. A styled download button that triggers the PDF
  5. A multi-section PDF with headers, tables, full-width rows, and footers
  6. Proper WordPress buffer handling to avoid “headers already sent” errors

 

FPDF PDF Generation Architecture

Step 1: Install FPDF – Three Methods

Method A: Via Composer (Recommended for Developers)

If your host supports SSH access:

cd /var/www/html/your-wordpress-site/
composer require setasign/fpdf

 

Then in your plugin:

require_once __DIR__ . '/vendor/autoload.php';
use FPDF\FPDF;

 

Method B: Manual Download (Most Common on Shared Hosting)

  1. Go to https://www.fpdf.org/en/download.php
  2. Download the latest release (currently 1.86)
  3. Extract and place fpdf.php inside your plugin or theme
/wp-content/plugins/custom-pdf-generator/
β”œβ”€β”€ custom-pdf-generator.php   ← Plugin entry file
β”œβ”€β”€ fpdf/
β”‚   └── fpdf.php               ← FPDF library
β”œβ”€β”€ includes/
β”‚   └── generate-pdf.php       ← PDF logic
└── assets/
    └── style.css              ← Button styles (optional)

 

Method C: Load from a Theme (Quick & Dirty for Testing)

// In functions.php β€” fine for testing, use a plugin for production
require_once get_template_directory() . '/lib/fpdf/fpdf.php';

Best Practice: Always use a plugin, not your theme, for PDF generation logic. Theme changes will wipe your code.

 

Step 2: Create the Plugin Scaffold

Create the main plugin file. This is what WordPress reads when you activate the plugin.

/wp-content/plugins/custom-pdf-generator/custom-pdf-generator.php

<?php
/**
 * Plugin Name:  Custom PDF Generator
 * Plugin URI:   https://yoursite.com
 * Description:  Generate downloadable PDFs from WordPress using FPDF.
 * Version:      1.2.0
 * Author:       Your Name
 * License:      GPL2
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Prevent direct access
}

// Define plugin constants
define( 'CPG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'CPG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

// Load FPDF
require_once CPG_PLUGIN_DIR . 'fpdf/fpdf.php';

// Load PDF generator logic
require_once CPG_PLUGIN_DIR . 'includes/generate-pdf.php';

// Load shortcode
require_once CPG_PLUGIN_DIR . 'includes/shortcode.php';

// Enqueue button styles
add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_style(
        'cpg-styles',
        CPG_PLUGIN_URL . 'assets/style.css',
        [],
        '1.2.0'
    );
});

 

Step 3: Build the Core PDF Generator

This is the heart of the plugin. We`ll build a reusable function that creates a well-structured, multi-section PDF document.

/wp-content/plugins/custom-pdf-generator/includes/generate-pdf.php

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Extends FPDF to add a custom header and footer on every page.
 */
class CustomPDF extends FPDF {

    private string $documentTitle;

    public function __construct( string $title = 'Document' ) {
        parent::__construct();
        $this->documentTitle = $title;
    }

    // Automatic header on every page
    public function Header() {
        // Logo (optional β€” comment out if you don't have one)
        // $this->Image( CPG_PLUGIN_DIR . 'assets/logo.png', 10, 6, 30 );

        // Brand color header bar
        $this->SetFillColor( 30, 64, 175 ); // Blue-700
        $this->Rect( 0, 0, 210, 18, 'F' );

        // Title text
        $this->SetFont( 'Arial', 'B', 13 );
        $this->SetTextColor( 255, 255, 255 );
        $this->SetY( 4 );
        $this->Cell( 0, 10, $this->documentTitle, 0, 0, 'C' );

        // Reset text color and add spacing
        $this->SetTextColor( 30, 30, 30 );
        $this->Ln( 16 );
    }

    // Automatic footer on every page
    public function Footer() {
        $this->SetY( -15 );
        $this->SetFont( 'Arial', 'I', 8 );
        $this->SetTextColor( 120, 120, 120 );
        $this->Cell( 0, 10, 'Page ' . $this->PageNo() . ' | Generated on ' . date('d M Y'), 0, 0, 'C' );
    }
}

/**
 * Main PDF generation function.
 * Call this when the user clicks the download button.
 *
 * @param array $data  Optional dynamic data to render in the PDF.
 */
function cpg_generate_pdf( array $data = [] ): void {

    // Discard any HTML already sent to the browser
    // (critical in WordPress to prevent "headers already sent" errors)
    while ( ob_get_level() ) {
        ob_end_clean();
    }

    $pdf = new CustomPDF( 'Session Summary Report' );
    $pdf->SetMargins( 15, 10, 15 );
    $pdf->SetAutoPageBreak( true, 20 );
    $pdf->AddPage();

    // ─── Section 1: Document Meta ─────────────────────────────────────────────
    $pdf->SetFont( 'Arial', '', 9 );
    $pdf->SetTextColor( 100, 100, 100 );
    $pdf->Cell( 0, 6, 'Generated by: ' . get_bloginfo('name') . ' | Date: ' . date('d F Y, H:i'), 0, 1, 'R' );
    $pdf->Ln( 2 );

    // ─── Section 2: Section Heading ───────────────────────────────────────────
    cpg_section_heading( $pdf, 'Brief Summary of Session' );

    // Sample data β€” replace this with data from $wpdb, get_post_meta(), ACF, etc.
    $session_points = $data['session_points'] ?? [
        [ 'title' => 'Opening Remarks & Goals for the Quarter' ],
        [ 'title' => 'Review of Previous Action Items and Outcomes' ],
        [ 'title' => 'Key Metrics and Performance Overview' ],
        [ 'title' => 'Blockers, Risks, and Escalations Discussed' ],
        [ 'title' => 'Next Steps and Owner Assignments' ],
    ];

    cpg_render_full_width_list( $pdf, $session_points );
    $pdf->Ln( 4 );

    // ─── Section 3: Data Table ────────────────────────────────────────────────
    cpg_section_heading( $pdf, 'Action Items' );
    cpg_render_table(
        $pdf,
        [ 'Task', 'Owner', 'Due Date', 'Status' ],
        [
            [ 'Finalize Q3 budget report', 'Alice M.', '25 Jul 2025', 'In Progress' ],
            [ 'Update website homepage copy', 'Bob K.',   '30 Jul 2025', 'Pending'     ],
            [ 'Send client onboarding email', 'Carol S.', '22 Jul 2025', 'Completed'   ],
            [ 'Fix login page bug on mobile', 'Dev Team', '28 Jul 2025', 'In Progress' ],
        ]
    );
    $pdf->Ln( 4 );

    // ─── Section 4: Notes / Free Text ────────────────────────────────────────
    cpg_section_heading( $pdf, 'Additional Notes' );
    $notes = $data['notes'] ?? 'Next session is scheduled for the first Monday of next month. All team members are expected to submit their individual progress reports 48 hours in advance.';
    $full_width = $pdf->GetPageWidth() - $pdf->lMargin - $pdf->rMargin;
    $pdf->SetFont( 'Arial', '', 10 );
    $pdf->SetTextColor( 50, 50, 50 );
    $pdf->MultiCell( $full_width, 6, utf8_decode( $notes ), 1, 'L' );

    // ─── Output ───────────────────────────────────────────────────────────────
    // 'D' = force download | 'I' = show in browser | 'F' = save to server
    $pdf->Output( 'D', 'session-summary-' . date('Ymd') . '.pdf' );
    exit;
}

/**
 * Renders a styled section heading.
 */
function cpg_section_heading( FPDF $pdf, string $title ): void {
    $full_width = $pdf->GetPageWidth() - $pdf->lMargin - $pdf->rMargin;
    $pdf->SetFont( 'Arial', 'B', 11 );
    $pdf->SetFillColor( 239, 246, 255 );  // Light blue background
    $pdf->SetTextColor( 30, 64, 175 );    // Blue text
    $pdf->SetDrawColor( 147, 197, 253 );  // Blue border
    $pdf->Cell( $full_width, 8, utf8_decode( $title ), 1, 1, 'L', true );
    $pdf->SetTextColor( 30, 30, 30 );
    $pdf->SetDrawColor( 0, 0, 0 );
    $pdf->Ln( 2 );
}

/**
 * Renders a full-width bulleted list of items.
 *
 * @param FPDF  $pdf
 * @param array $items  Each item: ['title' => 'Some text']
 */
function cpg_render_full_width_list( FPDF $pdf, array $items ): void {
    $full_width = $pdf->GetPageWidth() - $pdf->lMargin - $pdf->rMargin;
    $pdf->SetFont( 'Arial', '', 10 );
    $pdf->SetFillColor( 255, 255, 255 );
    $pdf->SetTextColor( 50, 50, 50 );

    foreach ( $items as $index => $item ) {
        $fill = ( $index % 2 === 0 ); // Alternating row shading
        if ( $fill ) {
            $pdf->SetFillColor( 248, 250, 252 );
        } else {
            $pdf->SetFillColor( 255, 255, 255 );
        }

        $text = '  β€’ ' . utf8_decode( $item['title'] );
        $pdf->MultiCell( $full_width, 7, $text, 'LRB', 'L', true );
    }
}

/**
 * Renders a formatted multi-column table.
 *
 * @param FPDF     $pdf
 * @param string[] $headers   Column header labels
 * @param array[]  $rows      2D array of row data
 */
function cpg_render_table( FPDF $pdf, array $headers, array $rows ): void {
    $full_width  = $pdf->GetPageWidth() - $pdf->lMargin - $pdf->rMargin;
    $col_count   = count( $headers );
    $col_width   = $full_width / $col_count;

    // Header row
    $pdf->SetFont( 'Arial', 'B', 10 );
    $pdf->SetFillColor( 30, 64, 175 );
    $pdf->SetTextColor( 255, 255, 255 );
    foreach ( $headers as $header ) {
        $pdf->Cell( $col_width, 8, $header, 1, 0, 'C', true );
    }
    $pdf->Ln();

    // Data rows
    $pdf->SetFont( 'Arial', '', 9 );
    $pdf->SetTextColor( 40, 40, 40 );
    foreach ( $rows as $i => $row ) {
        // Zebra striping
        $pdf->SetFillColor( $i % 2 === 0 ? 248 : 255, $i % 2 === 0 ? 250 : 255, $i % 2 === 0 ? 252 : 255 );

        // Status coloring
        foreach ( $row as $j => $cell ) {
            if ( $j === 3 ) { // Status column
                if ( $cell === 'Completed' ) {
                    $pdf->SetTextColor( 22, 101, 52 ); // Green
                } elseif ( $cell === 'In Progress' ) {
                    $pdf->SetTextColor( 120, 53, 15 ); // Amber
                } else {
                    $pdf->SetTextColor( 30, 30, 30 );
                }
            } else {
                $pdf->SetTextColor( 40, 40, 40 );
            }
            $pdf->Cell( $col_width, 7, utf8_decode( $cell ), 1, 0, 'L', true );
        }
        $pdf->Ln();
    }

    $pdf->SetTextColor( 30, 30, 30 );
}

 

Step 4: Register the Shortcode

/wp-content/plugins/custom-pdf-generator/includes/shortcode.php

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

add_shortcode( 'generate_pdf', 'cpg_pdf_shortcode' );

function cpg_pdf_shortcode( $atts ): string {

    // Merge shortcode attributes with defaults
    $atts = shortcode_atts([
        'label'    => 'Download PDF Report',
        'post_id'  => get_the_ID(),
        'filename' => 'report',
    ], $atts, 'generate_pdf' );

    // Handle PDF generation request
    if ( isset( $_GET['cpg_download'] ) && $_GET['cpg_download'] === '1' ) {

        // Security: verify nonce
        if ( ! isset( $_GET['cpg_nonce'] ) || ! wp_verify_nonce( $_GET['cpg_nonce'], 'cpg_download_pdf' ) ) {
            wp_die( 'Security check failed. Please try again.' );
        }

        // Pull dynamic data from WordPress (example: custom fields)
        $post_id = absint( $_GET['post_id'] ?? get_the_ID() );

        $data = [
            'session_points' => [
                [ 'title' => get_post_meta( $post_id, 'session_point_1', true ) ?: 'Opening Discussion' ],
                [ 'title' => get_post_meta( $post_id, 'session_point_2', true ) ?: 'Key Decisions Made' ],
                [ 'title' => get_post_meta( $post_id, 'session_point_3', true ) ?: 'Action Items Assigned' ],
                [ 'title' => get_post_meta( $post_id, 'session_point_4', true ) ?: 'Next Meeting Agenda' ],
            ],
            'notes' => get_post_meta( $post_id, 'session_notes', true ) ?: 'No additional notes.',
        ];

        cpg_generate_pdf( $data );
        // cpg_generate_pdf() calls exit(), so nothing below runs on PDF requests
    }

    // Build the download URL with a nonce
    $download_url = add_query_arg([
        'cpg_download' => '1',
        'post_id'      => get_the_ID(),
        'cpg_nonce'    => wp_create_nonce( 'cpg_download_pdf' ),
    ]);

    // Return the download button HTML
    ob_start();
    ?>
    <div class="cpg-download-wrapper">
        <a href="<?php echo esc_url( $download_url ); ?>"
           class="cpg-download-btn"
           title="<?php echo esc_attr( $atts['label'] ); ?>">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right:6px;vertical-align:middle;">
                <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
                <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
            </svg>
            <?php echo esc_html( $atts['label'] ); ?>
        </a>
        <p class="cpg-note">PDF is generated in real-time from live data.</p>
    </div>
    <?php
    return ob_get_clean();
}

 

Step 5: Style the Download Button

/wp-content/plugins/custom-pdf-generator/assets/style.css

.cpg-download-wrapper {
    margin: 24px 0;
    text-align: left;
}

.cpg-download-btn {
    display: inline-flex;
    align-items: center;
    background: #1e40af;
    color: #ffffff !important;
    padding: 12px 24px;
    border-radius: 8px;
    text-decoration: none !important;
    font-weight: 600;
    font-size: 0.95rem;
    transition: background 0.2s ease, transform 0.1s ease;
    box-shadow: 0 4px 12px rgba(30, 64, 175, 0.3);
}

.cpg-download-btn:hover {
    background: #1d4ed8;
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(30, 64, 175, 0.4);
}

.cpg-download-btn:active {
    transform: translateY(0);
}

.cpg-note {
    margin-top: 8px;
    font-size: 0.8rem;
    color: #6b7280;
}

 

Step 6: Use It in WordPress

Place the shortcode in any page or post:

[generate_pdf]

 

Or customize the label:

[generate_pdf label="Download Session Report"]

 

For a WooCommerce order PDF, you can pass the order ID:

[generate_pdf label="Download Invoice" post_id="1234"]

 

Solving the “Headers Already Sent” Problem in WordPress

This is the most common error developers hit when generating PDFs inside WordPress. It happens because WordPress (themes, plugins, hooks) outputs HTML before your $pdf->Output() call β€” and PDF output requires a clean HTTP response.

The fix:

// Always do this BEFORE calling $pdf->Output()
while ( ob_get_level() ) {
    ob_end_clean();
}

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

 

WordPress uses output buffering (ob_start()) throughout its render pipeline. The while loop cleanly empties and closes all active buffer levels, giving your PDF a clean output channel.

If you still get errors, check for:

  • BOM characters at the start of PHP files (open in a hex editor and look for EF BB BF)
  • echo or print_r calls before Output() in your debug code
  • Plugin conflicts that echo output on init or template_redirect

 

Pulling Dynamic Data from WordPress

The real power of this setup is connecting the PDF to live WordPress data. Here are practical patterns:

From Custom Post Meta (ACF or native)

$title   = get_post_meta( $post_id, 'report_title', true );
$items   = get_post_meta( $post_id, 'report_items', true ); // ACF repeater returns array
$summary = get_post_meta( $post_id, 'report_summary', true );

 

From a Custom Database Table

global $wpdb;

$rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT task, owner, due_date, status FROM {$wpdb->prefix}action_items WHERE session_id = %d",
        $session_id
    ),
    ARRAY_A
);

 

From WooCommerce Order Data

$order    = wc_get_order( $order_id );
$items    = $order->get_items();
$total    = $order->get_total();
$customer = $order->get_billing_first_name() . ' ' . $order->get_billing_last_name();

 

Unicode & Special Characters β€” The utf8_decode() Trick

FPDF`s built-in fonts (Helvetica, Arial, Times) only support Latin-1 characters. If your content includes accented characters (Γ©, Γ±, ΓΌ) or special symbols, you must decode from UTF-8:

$pdf->Cell( $width, 8, utf8_decode( $your_string ) );
$pdf->MultiCell( $width, 6, utf8_decode( $your_string ), 1 );

 

For full Unicode support (Hindi, Arabic, Chinese, etc.)

Use the FPDF-UTF8 extension or switch to TCPDF/mPDF which have native multibyte support built in. For Indian language support specifically (Hindi, Tamil, Telugu), mPDF with appropriate fonts is the best choice.

 

Advanced: Save PDFs to the Server (Not Just Download)

Sometimes you want to save the generated PDF as a file β€” for archiving, emailing, or later retrieval:

$upload_dir = wp_upload_dir();
$pdf_path   = $upload_dir['path'] . '/report-' . $post_id . '-' . date('Ymd') . '.pdf';

$pdf->Output( 'F', $pdf_path ); // F = save to file path
// Don't exit β€” continue with saving the file path to post meta

update_post_meta( $post_id, '_generated_pdf_path', $pdf_path );
update_post_meta( $post_id, '_generated_pdf_url', $upload_dir['url'] . '/' . basename($pdf_path) );

Then retrieve and link to it later:

$pdf_url = get_post_meta( $post_id, '_generated_pdf_url', true );
if ( $pdf_url ) {
    echo '<a href="' . esc_url( $pdf_url ) . '" target="_blank">View Saved PDF</a>';
}

 

Security Best Practices

PDF endpoints are a common attack surface. Here’s how to lock yours down:

1. Always Use Nonces

// Create nonce when generating the link
wp_create_nonce( 'cpg_download_pdf' )

// Verify it before generating the PDF
wp_verify_nonce( $_GET['cpg_nonce'], 'cpg_download_pdf' )

2. Sanitize All Input

$post_id = absint( $_GET['post_id'] ?? 0 );       // Integer IDs
$name    = sanitize_text_field( $_GET['name'] );   // Text strings

3. Restrict Access When Needed

// For logged-in users only:
if ( ! is_user_logged_in() ) {
    wp_die( 'You must be logged in to download this PDF.' );
}

// For specific capability:
if ( ! current_user_can( 'edit_posts' ) ) {
    wp_die( 'Access denied.' );
}

4. Rate Limiting (Simple)

$transient_key = 'pdf_requests_' . get_current_user_id();
$count         = (int) get_transient( $transient_key );

if ( $count > 10 ) {
    wp_die( 'Too many PDF requests. Please wait a minute.' );
}

set_transient( $transient_key, $count + 1, 60 ); // 10 per 60 seconds

 

Common Errors and Fixes

Error Cause Fix
Cannot modify header information β€” headers already sent HTML output before Output() Use ob_end_clean() loop before Output()
Cannot access protected property PDF_Table::$lMargin Accessing margin from outside class Use $pdf->GetPageWidth() – $pdf->lMargin inside the class only
PDF opens blank in browser Buffer not cleared properly Ensure all ob_* levels are cleared
Characters showing as ? or boxes UTF-8 encoding mismatch Use utf8_decode() for Latin, or switch to mPDF for Unicode
PDF not downloading (just opening) Wrong Output mode Use Output(‘D’, ‘name.pdf’) to force download
FPDF class not found Autoload path wrong Double-check require_once path using CPG_PLUGIN_DIR constant
PDF generates locally but not on server File permissions Ensure /wp-content/uploads/ is writable (chmod 755)

 

Complete Plugin File Tree

/wp-content/plugins/custom-pdf-generator/
β”œβ”€β”€ custom-pdf-generator.php     ← Main plugin file
β”œβ”€β”€ fpdf/
β”‚   └── fpdf.php                 ← FPDF library
β”œβ”€β”€ includes/
β”‚   β”œβ”€β”€ generate-pdf.php         ← CustomPDF class + rendering functions
β”‚   └── shortcode.php            ← [generate_pdf] shortcode registration
β”œβ”€β”€ assets/
β”‚   └── style.css                ← Download button styles
└── readme.txt                   ← WordPress plugin readme (optional)

 

Extending the Plugin β€” Ideas to Build On

Once your base plugin is working, here are high-value features to add next:

1. WooCommerce Invoice PDFs β€” Hook into woocommerce_order_status_completed to auto-generate and email a PDF invoice every time an order is placed.

2. User-Specific PDF Downloads β€” Let each logged-in user download their own profile summary, purchase history, or certificate.

3. Certificate Generator β€” Use FPDF’s Image() method to overlay text on a certificate background image. Perfect for LMS plugins like LearnDash or LifterLMS.

4. Scheduled PDF Reports β€” Use WordPress Cron (wp_schedule_event) to auto-generate weekly or monthly summary PDFs and email them to admins.

5. AJAX-Based PDF Preview β€” Generate a PDF server-side and stream it into an <iframe> using AJAX for a live preview before download.

6. Bulk PDF Generation β€” Add a WP-Admin bulk action to generate PDFs for multiple posts/orders at once.

 

Second Methods: Project Option

Option 1: Install FPDF via Composer (Recommended)

  1. Check if your WordPress hosting allows Composer.
    SSH into your server and go to your WP root (where wp-config.php is).

    cd /var/www/html/your-wordpress-site/
    
  2. Require FPDF with Composer:

     composer require setasign/fpdf

    This will add it inside vendor/.

  3. Include FPDF in your theme/plugin PHP file:

    require_once __DIR__ . '/vendor/autoload.php';
    
    use FPDF\FPDF;
    
    $pdf = new FPDF();
    $pdf->AddPage();
    $pdf->SetFont('Arial','B',16);
    $pdf->Cell(40,10,'Hello WordPress + FPDF!');
    $pdf->Output();
    

Option 2: Manually Add FPDF

If you don’t have Composer access:

  1. Download FPDF library
    πŸ‘‰ https://www.fpdf.org/en/download.php

  2. Extract and place the fpdf.php file in your theme or plugin folder.
    Example: /wp-content/themes/your-theme/lib/fpdf/

  3. Include it in your code:

     
    require get_template_directory() . '/lib/fpdf/fpdf.php';
    
    $pdf = new FPDF();
    $pdf->AddPage();
    $pdf->SetFont('Arial','B',16);
    $pdf->Cell(40,10,'FPDF in WordPress!');
    $pdf->Output();
    
    

Option 3: Create a Small Plugin

If you want to keep things neat:

    1. Create a folder /wp-content/plugins/wp-fpdf/.

    2. Inside it, create wp-fpdf.php:

       
      <?php
      /*
      Plugin Name: WP FPDF
      Description: Generate PDFs using FPDF inside WordPress.
      Version: 1.0
      */
      
      require_once plugin_dir_path(__FILE__) . 'fpdf/fpdf.php';
      
    3. Put the fpdf.php file in /wp-content/plugins/wp-fpdf/fpdf/.

  1. Activate the plugin in WP Admin β†’ Plugins.

Since WordPress runs PHP the same way, the PDF generation code (using FPDF / TCPDF / mPDF) can be integrated. The β€œCannot access protected property PDF_Table::$lMargin” issue is not WordPress-specific β€” it comes from how the library is written.

Here’s how you can integrate properly in WordPress:

4. Load the PDF library

  • Put fpdf.php (or your extended PDF_Table.php) inside your theme or a custom plugin folder.

/wp-content/plugins/custom-pdf/
fpdf.php
pdf-table.php
custom-pdf.php

In custom-pdf.php (your plugin main file):

/**
* Plugin Name: Custom PDF Generator
*/
require_once plugin_dir_path(__FILE__) . 'fpdf.php';
require_once plugin_dir_path(__FILE__) . 'pdf-table.php';

5. Create a shortcode for generating PDFs

You can make a shortcode so you can add [generate_pdf] anywhere in posts/pages:

add_shortcode('generate_pdf', function() {
if (isset($_GET['download_pdf'])) {
// Include PDF class
$pdf = new PDF_Table();
$pdf-&gt;AddPage();

// Title
$pdf-&gt;SetFont('Arial','B',12);
$pdf-&gt;Cell(0,10,utf8_decode("Brief Summary of Session"),0,1,'L',true);

// Sample JSON (replace with dynamic data from DB or WP fields)
$point6f = '[{"PointA_title":"k1 Brief of Session"},{"PointA_title":"k2 Brief of Session"}]';
$datapoint6f = json_decode($point6f, true);

$pdf-&gt;SetFont('Arial','',10);
$fullWidth = $pdf-&gt;GetPageWidth() - $pdf-&gt;lMargin - $pdf-&gt;rMargin; // works inside class methods!

foreach ($datapoint6f as $item) {
$title = htmlspecialchars($item['PointA_title']);
$pdf-&gt;MultiCell($fullWidth, 6, utf8_decode($title), 1, 'L');
$pdf-&gt;Ln(2);
}

$pdf-&gt;Output('D', 'session-summary.pdf'); // Force download
exit;
}

// Button to trigger PDF
return '<a class="button" href="?download_pdf=1">Download PDF</a>';
});

 

6. Use in WordPress

  • Place [generate_pdf] inside a page.

  • It will show a button β†’ clicking it generates and downloads the PDF with full-width rows.

FPDF vs TCPDF vs mPDF β€” Full Decision Guide

Use this table to pick the right library for your next project:

Use Case Best Library
Simple tables, invoices, reports FPDF
Complex HTML-to-PDF with CSS mPDF
Very large documents (100+ pages) TCPDF
RTL languages (Arabic, Hebrew) mPDF
Indian/Asian language scripts mPDF
Barcode / QR code in PDF TCPDF
Shared hosting (no Composer) FPDF
WordPress + WooCommerce invoices FPDF or mPDF

 

FPDF is one of those quiet, powerful tools that WordPress developers often overlook in favour of bloated plugins. But once you understand the ob_end_clean() trick for WordPress compatibility, the utf8_decode() requirement for text, and the plugin structure for clean code organization β€” you have a custom PDF engine that is:

  • Free β€” no licensing, no monthly fees
  • Private β€” data never leaves your server
  • Fast β€” no external API calls
  • Flexible β€” full pixel-level layout control

Whether you are generating invoices, session reports, certificates, or client proposals β€” this approach scales from a single shortcode to a full PDF microservice inside WordPress.

Use the plugin structure from this guide as your foundation, swap in your own data sources, and you’ll ship your first custom PDF feature in under an hour.

 

Frequently Asked Questions

+

1. What is FPDF in PHP?

FPDF is a free PHP library that allows developers to generate PDF documents dynamically without requiring external extensions. It is widely used for invoices, certificates, reports, and downloadable documents.
+

2. Can FPDF be used in WordPress?

Yes. FPDF can be integrated into WordPress plugins, themes, or custom PHP scripts to generate PDF files from posts, forms, user data, and database records.
+

3. Is FPDF free to use?

Yes. FPDF is an open-source PHP library available free of charge for personal and commercial projects.
+

4. How do I install FPDF in WordPress?

You can download the FPDF library from its official website, upload it to your WordPress project, include the fpdf.php file, and start generating PDF documents using PHP.
+

5. What types of PDFs can be generated using FPDF?

FPDF can generate:

  • Invoices
  • Certificates
  • Student Reports
  • Event Tickets
  • Payment Receipts
  • Course Completion Certificates
  • Employee Documents
  • Business Reports
+

6. Can I add images and logos to FPDF documents?

Yes. FPDF supports adding images, company logos, signatures, and custom branding elements to PDF documents.
+

7. Does FPDF support custom fonts?

Yes. FPDF allows developers to add custom fonts and control font size, style, alignment, and formatting within PDF files.
+

8. Can I generate PDFs from WordPress form submissions?

Yes. FPDF can create PDF documents automatically from Contact Forms, registration forms, LMS forms, WooCommerce orders, and custom form submissions.
+

9. What is the difference between FPDF and TCPDF?

FPDF is lightweight and easy to use, while TCPDF offers advanced features such as HTML rendering, QR codes, barcodes, and digital signatures.
+

10. Can FPDF generate invoices in WordPress?

Yes. Many WordPress developers use FPDF to automatically generate customer invoices, payment receipts, and order summaries.
+

11. Is FPDF suitable for large PDF documents?

FPDF performs well for small to medium-sized PDF documents. For highly complex documents with advanced formatting, TCPDF or DomPDF may be more suitable.
+

12. Can I save generated PDFs on the server?

Yes. FPDF supports both downloading PDFs directly to the browser and saving PDF files to the server for future access.
+

13. How can I secure PDF generation in WordPress?

Best practices include:

  • Validating user input
  • Restricting unauthorized access
  • Using HTTPS
  • Sanitizing form data
  • Protecting sensitive documents with authentication
+

14. Can WooCommerce use FPDF for invoice generation?

Yes. FPDF can be integrated with WooCommerce to generate custom invoices, packing slips, shipping labels, and order reports.
+

15. Is FPDF compatible with PHP 8.x?

Yes. Recent versions of FPDF work with PHP 8.x, making it suitable for modern WordPress installations.
Previous Article

Create a chatbot in PHP using the Gemini API (from Google AI)

Next Article

How to Generate PDF with Full-Width Rows in WordPress Using FPDF

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨