setasign/fpdi
FPDI is a PHP library to import pages from existing PDFs and reuse them as templates in FPDF, TCPDF, or tFPDF. PSR-4 namespaced, Composer-friendly, no special PHP extensions required, with improved performance and lower memory use in v2.
## Getting Started
### Minimal Setup for Laravel
1. **Install Dependencies**
Add to `composer.json` (choose one PDF engine):
```json
"require": {
"setasign/fpdf": "1.9.*",
"setasign/fpdi": "^2.6",
"dompdf/dompdf": "^2.0" // Optional: For hybrid workflows
}
Run composer install.
Basic Usage in a Controller
use setasign\Fpdi\Fpdi;
public function generatePdf()
{
$pdf = new Fpdi();
$pdf->AddPage();
$pdf->setSourceFile(storage_path('app/templates/invoice_template.pdf'));
$tplId = $pdf->importPage(1); // Import first page
$pdf->useTemplate($tplId, 10, 10, 190); // Place template at (10,10) with width 190mm
// Add dynamic content (e.g., text, images)
$pdf->SetFont('Arial', 'B', 12);
$pdf->Cell(0, 10, 'Dynamic Content', 0, 1, 'C');
return $pdf->Output('invoice.pdf', 'D'); // Download
}
First Use Case: Invoice Generation
storage/app/templates/.$pdf->SetXY(50, 50); // Position cursor
$pdf->Cell(0, 10, "Client: {$client->name}", 0, 1);
Pattern: "Template + Overlay"
storage/app/templates/.$pdf = new Fpdi();
$pdf->AddPage();
$pdf->setSourceFile($templatePath);
$tplId = $pdf->importPage(1);
$pdf->useTemplate($tplId, 0, 0, 210); // Full-page template
// Overlay dynamic data
$pdf->SetFont('Helvetica', 'B', 12);
$pdf->SetXY(100, 100);
$pdf->Cell(0, 10, "Order #{$order->id}", 0, 1);
Multi-Page Templates:
$pageCount = $pdf->setSourceFile($templatePath);
for ($i = 1; $i <= $pageCount; $i++) {
$tplId = $pdf->importPage($i);
$pdf->AddPage();
$pdf->useTemplate($tplId, 0, 0, 210);
// Add page-specific content
}
[CLIENT_NAME]) in the source PDF, then overlay with FPDF:
$pdf->SetXY(50, 50);
$pdf->Cell(0, 10, str_replace('[CLIENT_NAME]', $client->name, '[CLIENT_NAME]'));
$pdf->Image(storage_path('app/logos/company.png'), 10, 10, 50);
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(Fpdi::class, function () {
return new Fpdi();
});
}
// app/Jobs/GeneratePdfJob.php
public function handle()
{
$pdf = resolve(Fpdi::class);
// ... generation logic
Storage::put('app/generated/report.pdf', $pdf->Output('', 'S'));
}
$pdf = new Fpdi();
$pdf->AddPage();
$pdf->setSourceFile($templatePath);
$tplId = $pdf->importPage(1);
$pdf->useTemplate($tplId, 0, 0, 210);
// Convert to DomPDF-compatible PDF
$dompdf = new Dompdf();
$dompdf->loadHtml($pdf->getHtmlContent());
$dompdf->render();
LONGTEXT in a templates table, then stream to FPDI:
$template = Template::find(1);
$pdf = new Fpdi();
$pdf->setSourceFileFromString($template->pdf_data);
$tplId = $pdf->importPage(1);
Page Units Mismatch
$size = $pdf->getTemplateSize($tplId);
$widthPt = $size['width'] * 2.83464567; // mm to pt
$pdf->useTemplate($tplId, 10, 10, $widthPt);
Memory Leaks with Large PDFs
$pdf->cleanUp();
Font Issues
useTemplate():
$pdf->AddFont('DejaVu', '', 'DejaVuSansCondensed.ttf');
$pdf->SetFont('DejaVu', '', 12);
Recursive PDFs (Malformed Structures)
setParserMode() to skip problematic objects:
$pdf->setParserMode(Fpdi::PARSEMODE_FULL); // Default
// or
$pdf->setParserMode(Fpdi::PARSEMODE_SIMPLE); // Skip complex structures
TCPDF-Specific Quirks
Fpdi class requires explicit namespace:
use setasign\Fpdi\Tcpdf\Fpdi; // Not \setasign\Fpdi\TcpdfFpdi (deprecated)
Log Parser Errors Enable debug mode to log PDF parsing issues:
$pdf->setDebug(true);
// Check Laravel logs for FPDI errors
Validate Source PDFs Use PDFBox to validate templates before integration.
Check Template Size
Always verify dimensions with getTemplateSize():
$size = $pdf->getTemplateSize($tplId);
if ($size['width'] > 210) {
throw new \Exception("Template too wide for A4!");
}
Reuse PDF Objects Cache imported templates in memory for batch processing:
$templates = [];
foreach ($orders as $order) {
if (!isset($templates[$order->template_id])) {
$templates[$order->template_id] = $pdf->importPage($order->template_id);
}
$pdf->useTemplate($templates[$order->template_id], ...);
}
Stream Templates For large templates, stream directly from storage:
$pdf->setSourceFileFromStream(fopen(storage_path('templates/large.pdf'), 'r'));
Disable Unused Features Skip unnecessary parsing (e.g., annotations) for speed:
$pdf->setParserMode(Fpdi::PARSEMODE_SIMPLE);
// Example: Add a custom filter for encrypted streams
$pdf->add
How can I help you explore Laravel packages today?