setasign/fpdi
FPDI is a free PHP PDF document importer that reads pages from existing PDFs and uses them as templates in FPDF, TCPDF, or tFPDF. PSR-4 compatible, no special extensions required, with improved performance and lower memory use in v2.
Installation: Add FPDI to your Laravel project via Composer, paired with your preferred PDF library (FPDF, TCPDF, or tFPDF). Example for FPDF:
composer require setasign/fpdf:1.8.* setasign/fpdi:^2.5
Basic Setup: Require the autoloader and initialize FPDI:
require_once('vendor/autoload.php');
use setasign\Fpdi\Fpdi;
$pdf = new Fpdi();
$pdf->AddPage();
First Use Case: Import a PDF template and place it on a new page:
$pdf->setSourceFile(storage_path('app/templates/invoice_template.pdf'));
$tplId = $pdf->importPage(1); // Import first page
$pdf->useTemplate($tplId, 10, 10, 100); // Place template at (10,10) with width 100mm
$pdf->Output('invoice.pdf');
storage_path()) for template management.Template Inheritance:
$pdf->setSourceFile($templatePath);
$tplId = $pdf->importPage(1);
$pdf->useTemplate($tplId, 10, 10, 200); // Full-page template
$pdf->SetFont('Arial', 'B', 12);
$pdf->SetXY(50, 50);
$pdf->Cell(0, 10, "Dynamic Content: " . $order->amount);
Multi-Page Documents:
$pdf->setSourceFile($sourcePdf);
$pageIds = [$pdf->importPage(1), $pdf->importPage(3)]; // Skip page 2
foreach ($pageIds as $id) {
$pdf->AddPage();
$pdf->useTemplate($id, 0, 0, 0); // Full-page, no scaling
}
Dynamic Data Overlay:
useTemplate() with offsets to position dynamic content (e.g., signatures, dates).$pdf->useTemplate($tplId, 10, 10, 150); // Template
$pdf->SetXY(160, 10);
$pdf->Cell(0, 10, "Signed: " . now()->format('Y-m-d'));
Laravel Storage:
Store templates in storage/app/public/templates/ and use Storage::disk('public')->path() to reference them.
$pdf->setSourceFile(storage_path('app/public/templates/contract.pdf'));
Dynamic Template Selection: Use Laravel’s service container to resolve template paths based on context (e.g., user role).
$templatePath = app('template.resolver')->resolve($user->role);
Error Handling: Wrap FPDI operations in try-catch blocks to handle malformed PDFs gracefully.
try {
$pdf->setSourceFile($path);
} catch (\setasign\Fpdi\PdfParser\PdfParserException $e) {
Log::error("Invalid PDF template: " . $e->getMessage());
abort(500, "Template error");
}
TCPDF/tFPDF:
Replace Fpdi with setasign\Fpdi\Tcpdf\Fpdi or setasign\Fpdi\Tfpdf\Fpdi for advanced features (e.g., Unicode support, interactive forms).
Page Boundaries:
/MediaBox paths starting with /. Use PdfReader\PageBoundaries constants instead.$pdf->useTemplate($tplId, 10, 10, 100, '', '', true, PdfReader\PageBoundaries::MEDIABOX);
Zero Width/Height:
0 for width/height in useTemplate() now throws InvalidArgumentException.null or omit the parameter to auto-scale:
$pdf->useTemplate($tplId, 10, 10, null, null); // Auto-scale
Memory Exhaustion:
memory_limit temporarily.FpdiTrait::cleanUp() to release resources:
$pdf->cleanUp();
Unit Inconsistencies:
PdfReader\PageBoundaries to enforce consistency.TCPDF Deprecation:
TcpdfFpdi is deprecated; use setasign\Fpdi\Tcpdf\Fpdi instead.Visual Inspection:
Use FPDI’s getTemplateSize() to debug template dimensions:
$size = $pdf->getTemplateSize($tplId);
Log::debug("Template size: " . $size['width'] . "x" . $size['height']);
Logging: Enable FPDI’s debug mode to log parsing issues:
$pdf->setDebug(true);
Common Exceptions:
PdfParserException: Malformed PDF (e.g., corrupted file).InvalidArgumentException: Invalid template ID or dimensions.RuntimeException: Resource exhaustion or unsupported features.Custom Filters:
Extend setasign\Fpdi\PdfParser\Filter\FilterInterface to support proprietary PDF filters (e.g., encryption).
Annotation Handling:
Override FpdiTrait::importAnnotations() to customize how links/bookmarks are imported.
Laravel Service Provider: Bind FPDI to Laravel’s container for dependency injection:
$this->app->bind('fpdi', function () {
return new \setasign\Fpdi\Fpdi();
});
Dynamic Template Caching: Cache imported templates in Laravel’s cache system to avoid reprocessing:
$cacheKey = 'fpdi_template_' . md5($templatePath);
if (!Cache::has($cacheKey)) {
$pdf->setSourceFile($templatePath);
Cache::put($cacheKey, $pdf->importPage(1), now()->addHours(1));
}
Stream Handling:
FPDI supports reading from strings/stream resources, but large files may still cause timeouts. Use StreamReader::readBytes() with chunked reading for huge PDFs.
FlateDecode Fallback:
The fallback decompression method (window size 31) may slow down rendering for compressed streams. Monitor performance with microtime():
$start = microtime(true);
$pdf->useTemplate($tplId, ...);
Log::debug("Template rendering time: " . (microtime(true) - $start) . "s");
PHP 8+ Compatibility:
FPDI v2.6+ drops support for PHP <7.2. Use declare(strict_types=1); in Laravel to enforce type safety.
How can I help you explore Laravel packages today?