Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Fpdi Laravel Package

setasign/fpdi

Import pages from existing PDFs and reuse them as templates in FPDF, TCPDF, or tFPDF. FPDI 2 is a modern, namespaced, PSR-4 compatible rewrite with major performance and memory improvements, no special PHP extensions required.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   Add FPDI to your Laravel project via Composer, paired with your preferred PDF library (FPDF, TCPDF, or tFPDF):
   ```bash
   composer require setasign/fpdf setasign/fpdi

For TCPDF:

composer require tecnickcom/tcpdf setasign/fpdi
  1. First Use Case: Import a PDF template and overlay dynamic content. Example with FPDF:

    use setasign\Fpdi\Fpdi;
    
    $pdf = new Fpdi();
    $pdf->AddPage();
    $pdf->setSourceFile(public_path('template.pdf'));
    $tplId = $pdf->importPage(1); // Import first page
    $pdf->useTemplate($tplId, 10, 10, 100); // Place template at (10,10) with 100mm width
    $pdf->SetFont('Arial');
    $pdf->SetXY(120, 50);
    $pdf->Write(0, 'Dynamic Content Here');
    $pdf->Output('output.pdf');
    
  2. Key Files:

    • Documentation: Official Manual
    • Source: vendor/setasign/fpdi/src/ (for advanced use cases).

Implementation Patterns

Core Workflows

  1. Template-Based PDF Generation:

    • Pattern: Use setSourceFile() to load a template, then importPage() to import specific pages. Overlay dynamic content with useTemplate().
    • Example:
      $pdf = new Fpdi();
      $pdf->AddPage();
      $pdf->setSourceFile(storage_path('templates/invoice.pdf'));
      $tplId = $pdf->importPage(1);
      $pdf->useTemplate($tplId, 10, 10, 180); // Full-page template
      $pdf->SetFont('Arial', 'B', 12);
      $pdf->SetXY(50, 50);
      $pdf->Cell(0, 10, "Order #{$order->id}");
      
  2. Dynamic Data Injection:

    • Pattern: Combine FPDI with Laravel’s Blade or dynamic data binding. Store templates in storage/app/templates/ and fetch them via Eloquent.
    • Example:
      $templatePath = storage_path("templates/{$user->plan}_contract.pdf");
      $pdf->setSourceFile($templatePath);
      $tplId = $pdf->importPage(1);
      $pdf->useTemplate($tplId, 0, 0, 210); // Full-page
      $pdf->SetFont('Times', '', 10);
      $pdf->SetXY(100, 100);
      $pdf->MultiCell(0, 5, $user->customTerms);
      
  3. Multi-Page Templates:

    • Pattern: Import multiple pages and manage them with importPage() and useTemplate().
    • Example:
      $pages = [1, 3, 5]; // Pages to import
      foreach ($pages as $page) {
          $tplId = $pdf->importPage($page);
          $pdf->AddPage();
          $pdf->useTemplate($tplId, 0, 0);
          // Add dynamic content per page
      }
      
  4. Integration with Laravel Services:

    • Pattern: Create a service class to abstract FPDI logic. Example:
      namespace App\Services;
      
      use setasign\Fpdi\Fpdi;
      
      class PdfGenerator {
          public function generateFromTemplate(string $templatePath, array $data): string {
              $pdf = new Fpdi();
              $pdf->AddPage();
              $pdf->setSourceFile($templatePath);
              $tplId = $pdf->importPage(1);
              $pdf->useTemplate($tplId, 0, 0);
      
              foreach ($data as $key => $value) {
                  $pdf->SetFont('Arial');
                  $pdf->SetXY(50, 50 + ($key * 10));
                  $pdf->Write(0, "$key: $value");
              }
              return $pdf->Output('S', 'generated.pdf');
          }
      }
      
    • Usage in Controller:
      $generator = new PdfGenerator();
      $pdfContent = $generator->generateFromTemplate(
          storage_path('templates/report.pdf'),
          ['user' => $user->name, 'date' => now()->format('Y-m-d')]
      );
      return response($pdfContent)->header('Content-Type', 'application/pdf');
      
  5. Handling Complex Templates:

    • Pattern: Use getTemplateSize() to calculate dimensions and setPageFormat() to match template sizes.
    • Example:
      $size = $pdf->getTemplateSize($tplId);
      $pdf->setPageFormat([$size['width'], $size['height']], 'P');
      

Gotchas and Tips

Common Pitfalls

  1. Template Size Mismatch:

    • Issue: useTemplate() fails if the template dimensions don’t match the page. Use getTemplateSize() to debug:
      $size = $pdf->getTemplateSize($tplId);
      error_log("Template size: {$size['width']}x{$size['height']}");
      
    • Fix: Ensure the target page format matches the template:
      $pdf->setPageFormat([$size['width'], $size['height']], 'P');
      
  2. Memory Exhaustion:

    • Issue: Large PDFs (e.g., multi-GB files) may cause memory issues. FPDI 2.x mitigates this but monitor usage.
    • Fix: Use streams for large files:
      $pdf->setSourceFile(Storage::disk('s3')->readStream('large_template.pdf'));
      
  3. Font and Encoding Issues:

    • Issue: Dynamic text may render incorrectly if the template uses custom fonts.
    • Fix: Reset fonts before adding dynamic content:
      $pdf->SetFont('Arial', '', 12); // Fallback to a standard font
      
  4. Recursive PDF Structures:

    • Issue: Malformed PDFs (e.g., circular references) may crash FPDI. Fixed in v2.6.4+ but test edge cases.
    • Fix: Validate templates with tools like PDFtk before use.
  5. TCPDF-Specific Quirks:

    • Issue: TCPDF’s Fpdi class (setasign\Fpdi\Tcpdf\Fpdi) has slight API differences (e.g., AddPage() vs AddPage()).
    • Fix: Use the correct namespace and method signatures:
      use setasign\Fpdi\Tcpdf\Fpdi;
      $pdf = new Fpdi('P', 'mm', 'A4');
      
  6. Dynamic Content Overlap:

    • Issue: Dynamic content may overlap template elements if coordinates are miscalculated.
    • Fix: Use getTemplateSize() to align content:
      $size = $pdf->getTemplateSize($tplId);
      $pdf->SetXY(10, $size['height'] - 30); // Place text at bottom
      

Debugging Tips

  1. Log Template Metadata: Use getInfo() to inspect PDF properties:

    $info = $pdf->getInfo();
    error_log("PDF Info: " . print_r($info, true));
    
  2. Visual Debugging:

    • Export intermediate steps to verify layout:
      $pdf->Output('debug_' . time() . '.pdf');
      
  3. Handle Exceptions: Wrap FPDI operations in try-catch blocks:

    try {
        $tplId = $pdf->importPage($pageNumber);
    } catch (\setasign\Fpdi\PdfParser\PdfParserException $e) {
        Log::error("Failed to import page: " . $e->getMessage());
        // Fallback to a default template
    }
    

Performance Optimization

  1. Reuse Template Objects: Cache imported templates if reused across multiple PDFs:

    static $cachedTemplates = [];
    if (!isset($cachedTemplates[$templatePath])) {
        $pdf->setSourceFile($templatePath);
        $cachedTemplates[$templatePath] = $pdf->importPage(1);
    }
    $tplId = $cachedTemplates[$templatePath];
    
  2. Stream Large Templates: Avoid loading entire PDFs into memory:

    $stream = Storage::disk('s3')->readStream('large_template.pdf');
    $pdf->setSourceFile($stream);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope