FPDF and FPDI
| Language | PHP (FPDF 5.3+, FPDI 7.2+) |
|---|---|
| License | FPDF: permissive (FPDF license, MIT on Packagist); FPDI: MIT |
| Repository | github.com/Setasign/FPDF (Composer mirror), github.com/Setasign/FPDI |
| Documentation | FPDF manual, FPDI manual |
| Depends on | ext-zlib (FPDF also lists ext-gd for images) |
| Read / write | FPDI imports pages of existing PDFs / yes |
FPDF is a single PHP class that writes PDFs: pages, the 14 standard fonts and TrueType fonts, text cells and multi-line cells with wrapping, lines, rectangles, images (JPEG, PNG, GIF, WebP), links and automatic page breaks. It’s small, fast and has no required dependencies. Text in the standard fonts must be in Windows-1252 encoding; for UTF-8 text use the tFPDF add-on or TCPDF/mPDF.
FPDI extends FPDF (or TCPDF, or tFPDF) with setSourceFile and importPage: it turns a page of an existing PDF into a template you can place on a new page and draw over. That’s the standard PHP way to merge PDFs, extract pages, or fill out a PDF form by writing text at fixed positions. The free FPDI parser doesn’t support PDFs with compressed cross-reference streams (PDF 1.5+ object streams); Setasign sells an add-on parser for those, or you can convert such files first with qpdf --object-streams=disable in.pdf out.pdf.
Installation#
composer require setasign/fpdf setasign/fpdi
Basic usage: create a PDF with FPDF#
<?php
require 'vendor/autoload.php';
$pdf = new FPDF(); // A4, portrait, millimeters
$pdf->SetTitle('Hello', true);
$pdf->AddPage();
$pdf->SetFont('Helvetica', 'B', 24);
$pdf->Cell(0, 15, 'Hello, PDF!', 0, 1); // width 0 = up to the right margin, 1 = new line after
$pdf->SetFont('Helvetica', '', 12);
$pdf->MultiCell(0, 6, str_repeat('FPDF wraps long text in MultiCell. ', 10));
$pdf->Cell(0, 6, mb_convert_encoding('Café', 'windows-1252', 'UTF-8'), 0, 1);
$pdf->Image('logo.png', 10, 100, 50); // x, y, width in mm
$pdf->Output('F', 'hello.pdf'); // 'I' inline, 'D' download, 'S' string
How to merge PDF files with FPDI#
<?php
require 'vendor/autoload.php';
use setasign\Fpdi\Fpdi;
$pdf = new Fpdi();
foreach (['a.pdf', 'b.pdf'] as $file) {
$pageCount = $pdf->setSourceFile($file);
for ($i = 1; $i <= $pageCount; $i++) {
$tpl = $pdf->importPage($i);
$size = $pdf->getTemplateSize($tpl);
$pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
$pdf->useTemplate($tpl);
}
}
$pdf->Output('F', 'merged.pdf');
Imported pages keep their vector content and text; annotations, links and form fields aren’t copied.
How to split a PDF into pages#
use setasign\Fpdi\Fpdi;
$pageCount = (new Fpdi())->setSourceFile('in.pdf');
for ($i = 1; $i <= $pageCount; $i++) {
$pdf = new Fpdi();
$pdf->setSourceFile('in.pdf');
$tpl = $pdf->importPage($i);
$pdf->AddPage();
$pdf->useTemplate($tpl, ['adjustPageSize' => true]);
$pdf->Output('F', "page-$i.pdf");
}
How to write text onto an existing PDF#
Import the page, then draw with the normal FPDF methods, for example a stamp or values for a non-fillable form:
use setasign\Fpdi\Fpdi;
$pdf = new Fpdi();
$pdf->setSourceFile('in.pdf');
$tpl = $pdf->importPage(1);
$pdf->AddPage();
$pdf->useTemplate($tpl, ['adjustPageSize' => true]);
$pdf->SetFont('Helvetica', 'B', 40);
$pdf->SetTextColor(255, 0, 0);
$pdf->SetXY(30, 120);
$pdf->Write(0, 'CONFIDENTIAL');
$pdf->Output('F', 'stamped.pdf');
Tips#
- FPDI throws
CrossReferenceExceptionfor files with compressed xref streams; catch it and fall back to qpdf or Ghostscript to rewrite the file. - Use
new \setasign\Fpdi\Tcpdf\Fpdi()to combine FPDI with TCPDF for Unicode text and encryption. - Add
AddFont()with font definition files generated by FPDF’smakefontutility to embed TrueType fonts.