How to Add PDF Export to Any PHP Project – Generate Reports and Invoices with FPDF

Build This Feature FPDF Library PDF Reports Invoice Generation No Composer Download Button 2026
Build This Feature — PHP Tutorial Series

How to Add PDF Export to Any PHP Project — Generate Reports and Invoices with FPDF

A “Download as PDF” button transforms a PHP management system from a web page into a professional tool. This tutorial shows you how to add PDF export to any project using FPDF — the most beginner-friendly PHP PDF library, requiring no Composer, no dependencies, and no complex setup. By the end you will have a working invoice PDF generator you can attach to any table in your database.

📄 No Composer required 📊 Invoice + table templates 💾 Download + email 🗒 Student report card template

Every PHP management system generates reports. Hospital systems need patient summary sheets. ISP systems need billing invoices. School systems need progress reports. Exam systems need results printouts. The common request that appears in every project is “can we download this as a PDF?”

FPDF is the answer for PHP beginners. It is a free, lightweight PHP class that generates PDF files programmatically. You do not need Composer, Laravel, or any framework. Download one file, include it, and start building PDFs in minutes. This tutorial builds three complete real-world PDF templates you can adapt for any project.

💡 FPDF vs TCPDF vs DomPDF — which should you use? FPDF is the easiest for beginners — minimal setup, no dependencies, builds PDFs cell by cell. TCPDF supports more features (Unicode fonts, images, barcodes) but has a steeper learning curve. DomPDF converts HTML to PDF which looks beautiful but is much slower. For student projects: start with FPDF. The skills transfer directly to TCPDF when you need more power.

What You Will Build — Preview

invoice_1042.pdf — Preview of what you will build
🌐 Swift Internet Services
support@swiftnet.com | 0800 123 456
INVOICE
Ref: INV-1042
Date: 20 April 2026
Due: 05 May 2026
Bill To: John Smith
Plan: 100Mbps Standard
DescriptionPeriodAmount
100Mbps Standard PlanApril 2026£29.99
Installation feeOne-time£0.00
Total Due: £29.99
Thank you for your business. Please make payment by the due date.
1Download FPDF
2Your First PDF
3Billing Invoice
4Data Table Report
5Add Download Button
6Email as Attachment

Step 1 — Download and Set Up FPDF

  1. Go to fpdf.org and click Download
  2. Unzip the downloaded file — you only need the fpdf.php file and the font/ folder
  3. Create a libs/fpdf/ folder in your project and copy both items there

Your project structure should look like this:

Project structure after adding FPDF

your-project/
├── config/dbconnection.php
├── libs/
│   └── fpdf/
│       ├── fpdf.php       ← the main FPDF class
│       └── font/          ← font files (helvetica, times, etc.)
├── pdf/
│   └── generate_invoice.php   ← NEW: your PDF generator
└── admin/billing.php           ← existing page with download button

Step 2 — Your First “Hello PDF” in 10 Lines

Before building a full invoice, run this minimal example to confirm FPDF is installed correctly. Visit it directly in your browser — it should automatically download or display a PDF.

PHP pdf/test.php — confirm FPDF works before building anything complex

<?php
require_once '../libs/fpdf/fpdf.php';

$pdf = new FPDF();       // Create new PDF object
$pdf->AddPage();        // Add the first page
$pdf->SetFont('Helvetica', 'B', 20); // Font family, style (B=Bold), size
$pdf->Cell(0, 15, 'FPDF is Working!', 0, 1, 'C');
$pdf->SetFont('Helvetica', '', 12);
$pdf->Cell(0, 10, 'Your PHP project can now generate PDFs.', 0, 1, 'C');
$pdf->Output(); // 'I' = display in browser, 'D' = force download, 'S' = return as string

Understanding FPDF’s Cell() method

Cell($w, $h, $txt, $border, $ln, $align) — this is the workhorse function you will use constantly:

ParameterMeaningCommon Values
$wWidth in mm. 0 = full page width0, 60, 90, 190
$hHeight in mm (row height)8, 10, 12, 15
$txtThe text to display‘Hello’, $variable
$borderCell border. 0=none, 1=all sides, ‘LTR’=specific sides0, 1, ‘B’, ‘LR’
$lnLine break after. 0=same line, 1=next line, 2=under previous0, 1
$alignText alignment‘L’, ‘C’, ‘R’

Step 3 — Complete Billing Invoice from Database

This generates a professional invoice PDF by fetching a specific bill from your database and formatting it with a header, billing details, line items table, and total.

PHP pdf/generate_invoice.php — complete billing invoice

<?php
require_once '../config/dbconnection.php';
require_once '../libs/fpdf/fpdf.php';

// ── Get bill ID from URL (e.g. /pdf/generate_invoice.php?id=42) ────
$bill_id = (int)($_GET['id'] ?? 0);
if (!$bill_id) { die('Invalid bill ID.'); }

// ── Fetch bill data from database ──────────────────────────────────
$stmt = mysqli_prepare($conn,
    "SELECT b.*, c.name AS customer_name, c.email AS customer_email,
            c.phone AS customer_phone, p.plan_name, p.price
     FROM bills b
     JOIN customers c ON b.customer_id = c.id
     JOIN internet_plans p ON c.plan_id = p.id
     WHERE b.id = ?");
mysqli_stmt_bind_param($stmt, 'i', $bill_id);
mysqli_stmt_execute($stmt);
$bill = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt));
if (!$bill) { die('Bill not found.'); }

// ── Build the PDF ──────────────────────────────────────────────────
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetMargins(15, 15, 15);

// HEADER — Company name + Invoice title
$pdf->SetFillColor(0, 87, 184);    // #0057b8 blue
$pdf->SetTextColor(255, 255, 255);
$pdf->SetFont('Helvetica', 'B', 16);
$pdf->Cell(0, 14, 'Swift Internet Services', 0, 1, 'L', true);
$pdf->SetFont('Helvetica', '', 9);
$pdf->SetTextColor(0, 0, 0);
$pdf->Ln(3);

// Invoice reference + date (two columns)
$pdf->SetFont('Helvetica', 'B', 14);
$pdf->Cell(90, 10, 'INVOICE', 0, 0, 'L');
$pdf->SetFont('Helvetica', '', 10);
$pdf->Cell(90, 10, 'Ref: INV-' . str_pad($bill_id, 4, '0', STR_PAD_LEFT), 0, 1, 'R');

$pdf->SetFont('Helvetica', '', 10);
$pdf->Cell(90, 8, 'support@swiftnet.com', 0, 0, 'L');
$pdf->Cell(90, 8, 'Date: ' . date('d M Y'), 0, 1, 'R');
$pdf->Cell(90, 8, '', 0, 0);
$pdf->Cell(90, 8, 'Due: ' . date('d M Y', strtotime('+14 days')), 0, 1, 'R');
$pdf->Ln(5);

// Divider line
$pdf->SetDrawColor(0, 87, 184);
$pdf->SetLineWidth(0.5);
$pdf->Line(15, $pdf->GetY(), 195, $pdf->GetY());
$pdf->Ln(4);

// Bill To section
$pdf->SetFont('Helvetica', 'B', 10);
$pdf->Cell(0, 7, 'BILL TO', 0, 1);
$pdf->SetFont('Helvetica', '', 10);
$pdf->Cell(0, 6, $bill['customer_name'], 0, 1);
$pdf->Cell(0, 6, $bill['customer_email'], 0, 1);
$pdf->Cell(0, 6, 'Plan: ' . $bill['plan_name'], 0, 1);
$pdf->Ln(5);

// Table header row
$pdf->SetFillColor(0, 87, 184);
$pdf->SetTextColor(255, 255, 255);
$pdf->SetFont('Helvetica', 'B', 10);
$pdf->Cell(90, 9, 'Description', 0, 0, 'L', true);
$pdf->Cell(50, 9, 'Period',      0, 0, 'C', true);
$pdf->Cell(40, 9, 'Amount',     0, 1, 'R', true);

// Table data rows
$pdf->SetTextColor(0, 0, 0);
$pdf->SetFont('Helvetica', '', 10);
$pdf->SetFillColor(240, 246, 255);
$pdf->Cell(90, 8, $bill['plan_name'], 'B', 0, 'L', true);
$pdf->Cell(50, 8, date('F Y', strtotime($bill['bill_month'])), 'B', 0, 'C', true);
$pdf->Cell(40, 8, '£' . number_format($bill['amount'], 2), 'B', 1, 'R', true);

$pdf->Ln(3);
$pdf->SetFont('Helvetica', 'B', 12);
$pdf->Cell(0, 10, 'Total Due: £' . number_format($bill['amount'], 2), 0, 1, 'R');

// Footer note
$pdf->Ln(8);
$pdf->SetFont('Helvetica', 'I', 9);
$pdf->SetTextColor(120, 120, 120);
$pdf->Cell(0, 6, 'Thank you for your business. Please pay by the due date shown above.', 0, 1, 'C');

// Output: 'D' = force download, 'I' = show in browser
$filename = 'Invoice_INV-' . str_pad($bill_id, 4, '0', STR_PAD_LEFT) . '.pdf';
$pdf->Output('D', $filename); // 'D' forces download to user's device

Step 4 — Multi-Row Data Table Report

This pattern generates a PDF with a table of all records from a database query — useful for customer reports, exam results, inventory summaries, or attendance records.

PHP pdf/customer_report.php — all customers as a PDF table

<?php
require_once '../config/dbconnection.php';
require_once '../libs/fpdf/fpdf.php';

$result = mysqli_query($conn,
    "SELECT c.id, c.name, c.email, p.plan_name, p.price, c.status
     FROM customers c
     JOIN internet_plans p ON c.plan_id = p.id
     ORDER BY c.name ASC");

$pdf = new FPDF('L', 'mm', 'A4'); // Landscape orientation for wide tables
$pdf->AddPage();
$pdf->SetMargins(10, 10, 10);
$pdf->SetAutoPageBreak(true, 15); // Auto-add pages when content overflows

// Report title
$pdf->SetFont('Helvetica', 'B', 14);
$pdf->Cell(0, 10, 'Customer Report — ' . date('d M Y'), 0, 1, 'C');
$pdf->SetFont('Helvetica', '', 9);
$pdf->Cell(0, 6, 'Generated by Swift Internet Services Management System', 0, 1, 'C');
$pdf->Ln(4);

// Column headers
$pdf->SetFillColor(0, 87, 184);
$pdf->SetTextColor(255, 255, 255);
$pdf->SetFont('Helvetica', 'B', 9);
$cols = ['ID'=>12, 'Customer Name'=>65, 'Email'=>75, 'Plan'=>50, 'Price'=>25, 'Status'=>30];
foreach ($cols as $name => $w) {
    $pdf->Cell($w, 8, $name, 0, 0, 'C', true);
}
$pdf->Ln();

// Data rows with alternating background
$pdf->SetTextColor(0, 0, 0);
$pdf->SetFont('Helvetica', '', 9);
$row_i = 0;
while ($row = mysqli_fetch_assoc($result)) {
    $fill = $row_i % 2 === 0;
    if ($fill) $pdf->SetFillColor(240, 246, 255);
    else       $pdf->SetFillColor(255, 255, 255);
    $pdf->Cell(12, 7, $row['id'],          'B', 0, 'C', $fill);
    $pdf->Cell(65, 7, $row['name'],        'B', 0, 'L', $fill);
    $pdf->Cell(75, 7, $row['email'],       'B', 0, 'L', $fill);
    $pdf->Cell(50, 7, $row['plan_name'],   'B', 0, 'C', $fill);
    $pdf->Cell(25, 7, '£'.$row['price'],   'B', 0, 'R', $fill);
    $pdf->Cell(30, 7, ucfirst($row['status']),'B', 1, 'C', $fill);
    $row_i++;
}

// Row count footer
$pdf->Ln(3);
$pdf->SetFont('Helvetica', 'I', 8);
$pdf->Cell(0, 5, 'Total records: ' . $row_i, 0, 1, 'R');

$pdf->Output('D', 'Customer_Report_' . date('Y-m-d') . '.pdf');

Step 5 — Add a Download Button to Your Existing Pages

Add this button anywhere on your billing management page. Clicking it calls the PDF generator with the bill’s ID and automatically downloads the PDF.

PHP Inside your bills table — one download button per row

<?php while ($bill = mysqli_fetch_assoc($bills_result)): ?>
<tr>
  <td><?= htmlspecialchars($bill['ref']) ?></td>
  <td><?= htmlspecialchars($bill['customer_name']) ?></td>
  <td>£<?= number_format($bill['amount'], 2) ?></td>
  <td><?= htmlspecialchars($bill['status']) ?></td>
  <td>
    <!-- This button opens the PDF generator in a new tab -->
    <a href="/pdf/generate_invoice.php?id=<?= (int)$bill['id'] ?>"
       target="_blank"
       class="btn btn-sm btn-outline-danger">
      📄 Download PDF
    </a>
  </td>
</tr>
<?php endwhile; ?>

Add a “Download Full Report” button to your reports page

PHP Button that downloads all records as a PDF

<!-- Add near the top of your customer list / reports page -->
<a href="/pdf/customer_report.php"
   class="btn btn-danger">
  📥 Download Customer Report (PDF)
</a>

<!-- Or with date filters passed as GET parameters: -->
<a href="/pdf/customer_report.php?from=2026-01-01&to=2026-12-31"
   class="btn btn-danger">
  📥 Download 2026 Annual Report
</a>

Step 6 — Send PDF as Email Attachment

PHP Generate PDF and email it — combines FPDF + PHPMailer

require_once '../libs/fpdf/fpdf.php';
require_once '../helpers/mailer.php';

function emailInvoicePDF($bill) {
    // Step 1: Generate the PDF and save to a temp file
    $pdf = new FPDF();
    $pdf->AddPage();
    // ... build your PDF here (same as Step 3) ...

    // Save to temp file (not the browser output)
    $tmpFile = sys_get_temp_dir() . '/invoice_' . $bill['id'] . '.pdf';
    $pdf->Output('F', $tmpFile); // 'F' = save to file path (not browser)

    // Step 2: Email it using the sendEmail() function from Post 2
    $emailBody = '<p>Dear ' . htmlspecialchars($bill['customer_name'])
               . ,</p><p>Please find your invoice attached.</p>';
    $result = sendEmail(
        $bill['customer_email'],
        $bill['customer_name'],
        'Your Invoice for ' . date('F Y'),
        $emailBody,
        '',
        [$tmpFile] // Pass temp file as attachment
    );

    // Step 3: Clean up temp file
    if (file_exists($tmpFile)) unlink($tmpFile);
    return $result;
}
✅ You now have all three PDF features: Individual invoice download (per bill, triggered by button click), bulk report download (all records, triggered by admin button), and email delivery with PDF attachment (combine with the PHPMailer tutorial from this series).

Frequently Asked Questions

Text with special characters (£, €, é, ñ) shows as garbled characters in my PDF.

FPDF’s core fonts (Helvetica, Arial, Times) have limited Unicode support. For special characters, use iconv() to convert the string: $pdf->Cell(0, 8, iconv('UTF-8', 'ISO-8859-1', $text)). For full Unicode support including Arabic, Chinese, and full European character sets, switch to TCPDF which has built-in UTF-8 support — the code structure is very similar to FPDF so migrating is straightforward.

My PDF outputs garbled characters instead of the PDF in the browser.

This is almost always caused by whitespace or output before the PDF headers. Check for: a BOM (byte order mark) at the top of your PHP file (common in Windows text editors — use UTF-8 without BOM), any echo or print statements before the PDF output, a blank line after the closing ?> tag, or extra whitespace at the beginning of included files. The PDF output requires that nothing at all has been sent to the browser before $pdf->Output() is called. If using output buffering (ob_start() at the top of the file), call ob_end_clean() just before the Output() call.

How do I add a page number to every page?

Extend the FPDF class and override the Footer() method: class MyPDF extends FPDF { function Footer() { $this->SetY(-12); $this->SetFont('Helvetica','I',8); $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); } }. Then use $pdf = new MyPDF(); $pdf->AliasNbPages(); $pdf->AddPage();. The AliasNbPages() call enables the {nb} placeholder for total page count.

Can I add my logo image to the PDF?

Yes. FPDF supports PNG, JPEG, and GIF images. Use: $pdf->Image('/path/to/logo.png', $x, $y, $width); where x and y are the position in mm from the top-left of the page, and width is the desired width in mm. Add it in your header section, typically in the top-left corner. Make sure the image path is an absolute server path (use $_SERVER['DOCUMENT_ROOT']) not a relative URL.


How to Send Emails with PHPMailer →

Email the generated PDF as an attachment

AJAX Live Search + Filter →

Let users filter before downloading the report

ISP Management System →

Add this invoice generator to your ISP project

Export Data to Excel with PHP →

Alternative: offer Excel downloads alongside PDFs

Last updated April 2026. FPDF 1.86 used. Tested on PHP 8.2 / XAMPP. FPDF available at fpdf.org (free, open source, no dependencies).

Leave a Comment

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

Scroll to Top