Need to merge multiple PDFs into one file in your PHP application? In this DevTip, we'll explore how to combine PDF documents programmatically using PHP with the FPDI and FPDF libraries. These tools provide a straightforward way to handle PDF manipulation directly on your server.

Prerequisites

Before you start, ensure your environment meets these requirements:

  • PHP 5.6 or higher (PHP 7+ recommended for better performance)
  • Composer for managing PHP dependencies
  • Basic understanding of PHP programming concepts
  • The Zlib PHP extension must be enabled (required by FPDI)

Install FPDI and FPDF

You can install both libraries using Composer. Navigate to your project directory in the terminal and run:

composer require setasign/fpdf:1.8.* setasign/fpdi:^2.0

Composer handles autoloading, making the classes immediately available in your PHP scripts.

Generate a PDF with FPDF

FPDF is a library for generating PDF documents from scratch using PHP. Here's a simple example that creates a one-page PDF file named hello_world.pdf containing the text “Hello World!”:

<?php
require 'vendor/autoload.php';

// Use the global namespace for FPDF
$pdf = new \FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 16);
$pdf->Cell(40, 10, 'Hello World!');
$pdf->Output('F', 'hello_world.pdf'); // 'F' saves to a local file

Import existing PDFs with FPDI

FPDI extends FPDF, adding the capability to import pages from existing PDF documents. You can then place these imported pages into the PDF you are generating with FPDF. This example imports the first page from existing.pdf and saves it as imported.pdf:

<?php
require 'vendor/autoload.php';

use setasign\Fpdi\Fpdi;

$pdf = new Fpdi();

// Specify the source PDF file
$pdf->setSourceFile('existing.pdf');
// Import the first page
$pageId = $pdf->importPage(1);

// Add a page to the new PDF
$pdf->AddPage();
// Use the imported page as a template
$pdf->useTemplate($pageId);

// Save the resulting PDF
$pdf->Output('F', 'imported.pdf');

Merge two PDFs

Combining the concepts, we can create a function to merge two PDF files page by page. This helper function takes two input file paths ($a, $b) and an output path ($out), reads all pages from both input files, and writes them sequentially into the output file merged.pdf. It includes basic error handling.

<?php
require 'vendor/autoload.php';

use setasign\Fpdi\Fpdi;
use setasign\Fpdi\PdfParser\PdfParserException;

function mergeTwo(string $a, string $b, string $out): bool {
    try {
        $pdf = new Fpdi();
        foreach ([$a, $b] as $file) {
            if (!is_readable($file)) {
                 error_log("Cannot read file: $file");
                 return false; // Or throw an exception
            }
            $pageCount = $pdf->setSourceFile($file);
            if ($pageCount === false) {
                 error_log("Could not set source file (invalid PDF?): $file");
                 return false;
            }

            for ($i = 1; $i <= $pageCount; $i++) {
                $tpl = $pdf->importPage($i);
                $size = $pdf->getTemplateSize($tpl);
                // Add a page with the same size and orientation
                $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
                $pdf->useTemplate($tpl);
            }
        }
        $pdf->Output('F', $out);
        return true;
    } catch (PdfParserException $e) {
        error_log('PDF Parser Error during merge: ' . $e->getMessage());
        return false;
    } catch (\Exception $e) { // Catch other potential exceptions
        error_log('General error during PDF merge: ' . $e->getMessage());
        return false;
    }
}

// Example usage:
// Ensure a.pdf and b.pdf exist
// mergeTwo('a.pdf', 'b.pdf', 'merged.pdf');

Merge any number of PDFs

Often, you'll need to merge a dynamic list of PDF files. This function accepts an array of file paths and merges them into a single output file. It includes checks for file readability and handles potential parsing errors.

<?php
require 'vendor/autoload.php';

use setasign\Fpdi\Fpdi;
use setasign\Fpdi\PdfParser\PdfParserException;

function mergeMany(array $files, string $out): bool {
    try {
        $pdf = new Fpdi();
        foreach ($files as $file) {
            if (!is_readable($file)) {
                error_log("Cannot read file, skipping: $file");
                continue; // Skip this file
            }

            $pageCount = $pdf->setSourceFile($file);
             if ($pageCount === false) {
                 error_log("Could not set source file (invalid PDF?), skipping: $file");
                 continue; // Skip this file
            }

            for ($p = 1; $p <= $pageCount; $p++) {
                $tpl = $pdf->importPage($p);
                $size = $pdf->getTemplateSize($tpl);
                $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
                $pdf->useTemplate($tpl);
            }
        }
        // Only output if at least one page was added
        if ($pdf->PageNo() > 0) {
             $pdf->Output('F', $out);
             return true;
        } else {
             error_log("No pages were successfully merged.");
             return false;
        }

    } catch (PdfParserException $e) {
        error_log("PDF Parser Error during mergeMany: " . $e->getMessage());
        return false;
    } catch (\Exception $e) {
        error_log("General error during mergeMany: " . $e->getMessage());
        return false;
    }
}

// Example usage:
// $pdfFiles = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf'];
// mergeMany($pdfFiles, 'combined_document.pdf');

Manage memory for large files

PDF processing, especially merging multiple or large documents, can be memory-intensive. Here are a few tips:

  • Increase PHP Limits: If you encounter memory exhaustion errors, consider increasing PHP's memory limit and execution time in your php.ini file or using ini_set() at the start of your script (use with caution on shared hosting):
    ini_set('memory_limit', '256M'); // Adjust as needed, e.g., '512M', '1G'
    ini_set('max_execution_time', '300'); // 5 minutes, adjust as needed
    
  • Release Resources: After generating the output PDF, explicitly unset the FPDI object and suggest garbage collection to free up memory, especially in long-running scripts or loops:
    // Assuming $pdf is your FPDI object after Output()
    unset($pdf);
    if (function_exists('gc_collect_cycles')) {
        gc_collect_cycles();
    }
    
  • Consider Alternatives: For very large files or high-volume merging where server-side processing might hit resource limits, offloading the task to a dedicated service can be more robust and scalable.

Use Transloadit's document processing

For production workloads—especially handling big files, merging many documents frequently, or needing more advanced features—offloading the task can be more efficient and scalable. Transloadit's /document/merge Robot is designed for this:

  • Merges multiple PDFs reliably.
  • Can handle password-protected PDFs (if passwords are provided).
  • Integrates into automated workflows with features like queuing and scaling for heavy jobs.
  • Processes files without impacting your server's memory or execution time limits.

You can explore this in our 🤖 /document/merge Robot.

Wrap-up

FPDI and FPDF offer a powerful combination to merge documents into one in PHP directly on your server, requiring only a few lines of code for basic tasks. They are excellent choices for quick implementations and moderate workloads. However, when dealing with large-scale PDF manipulation, high throughput requirements, or complex workflows involving potentially large files, consider leveraging a cloud-based service like Transloadit to handle the heavy lifting, ensuring your application remains performant and scalable.