Merge PDF files with PHP

PhpPdf

Php Problem Overview


My concept is - there are 10 pdf files in a website. User can select some pdf files and then select merge to create a single pdf file which contains the selected pages. How can i do this with php?

Php Solutions


Solution 1 - Php

Below is the php PDF merge command.

$fileArray= array("name1.pdf","name2.pdf","name3.pdf","name4.pdf");

$datadir = "save_path/";
$outputName = $datadir."merged.pdf";

$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$outputName ";
//Add each pdf file to the end of the command
foreach($fileArray as $file) {
	$cmd .= $file." ";
}
$result = shell_exec($cmd);

I forgot the link from where I found it, but it works fine.

Note: You should have gs (on linux and probably Mac), or Ghostscript (on windows) installed for this to work.

Solution 2 - Php

i suggest PDFMerger from github.com, so easy like ::

include 'PDFMerger.php';

$pdf = new PDFMerger;

$pdf->addPDF('samplepdfs/one.pdf', '1, 3, 4')
	->addPDF('samplepdfs/two.pdf', '1-2')
	->addPDF('samplepdfs/three.pdf', 'all')
	->merge('file', 'samplepdfs/TEST2.pdf'); // REPLACE 'file' WITH 'browser', 'download', 'string', or 'file' for output options
	
	

Solution 3 - Php

I've done this before. I had a pdf that I generated with fpdf, and I needed to add on a variable amount of PDFs to it.

So I already had an fpdf object and page set up (http://www.fpdf.org/) And I used fpdi to import the files (http://www.setasign.de/products/pdf-php-solutions/fpdi/) FDPI is added by extending the PDF class:

class PDF extends FPDI
{

} 



	$pdffile = "Filename.pdf";
	$pagecount = $pdf->setSourceFile($pdffile);  
	for($i=0; $i<$pagecount; $i++){
		$pdf->AddPage();  
		$tplidx = $pdf->importPage($i+1, '/MediaBox');
		$pdf->useTemplate($tplidx, 10, 10, 200); 
	}

This basically makes each pdf into an image to put into your other pdf. It worked amazingly well for what I needed it for.

Solution 4 - Php

$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=".$new." ".implode(" ", $files);
shell_exec($cmd);

A simplified version of Chauhan's answer

Solution 5 - Php

Both the accepted answer and even the FDPI homepage seem to give botched or incomplete examples. Here's mine which works and is easy to implement. As expected it requires fpdf and fpdi libraries:

require('fpdf.php');
require('fpdi.php');

$files = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf'];

$pdf = new FPDI();

// iterate over array of files and merge
foreach ($files as $file) {
	$pageCount = $pdf->setSourceFile($file);
	for ($i = 0; $i < $pageCount; $i++) {
		$tpl = $pdf->importPage($i + 1, '/MediaBox');
		$pdf->addPage();
		$pdf->useTemplate($tpl);
	}
}

// output the pdf as a file (http://www.fpdf.org/en/doc/output.htm)
$pdf->Output('F','merged.pdf');

Solution 6 - Php

I've had similar problem in my software. We've wanted to merge several PDF files into one PDF file and submit it to an outer service. We've been using the FPDI solution as shown in Christa's solution.

However, the input PDF's we've been using could be in version higher than 1.7. We've decided to evaluate the FPDI commercial add-on. However, it turned out that some of the documents scanned by our office copier were having malformed indexes, which crashed the commercial FPDI add-on. So we've decided to use Ghostscript solution as in Chauhan's answer.

But then we got some strange metadata in the output PDF properties.

Finally we've decided to join two solutions to get PDF's merged and downgraded by Ghostscript, but the metadata is set by FPDI. We don't know yet how it would work with some advanced formatted pdfs, but for scans we use it works just fine. Here's our class excerpt:

class MergedPDF extends \FPDI
{
    private $documentsPaths = array();

    public function Render()
    {
        $outputFileName = tempnam(sys_get_temp_dir(), 'merged');

        // merge files and save resulting file as PDF version 1.4 for FPDI compatibility
        $cmd = "/usr/bin/gs -q -dNOPAUSE -dBATCH -dCompatibilityLevel=1.4 -sDEVICE=pdfwrite -sOutputFile=$outputFileName";
        foreach ($this->getDocumentsPaths() as $pdfpath) {
            $cmd .= " $pdfpath ";
        }
        $result = shell_exec($cmd);
        $this->SetCreator('Your Software Name');
        $this->setPrintHeader(false);
        $numPages = $this->setSourceFile($outputFileName);
        for ($i = 1; $i <= $numPages; $i++) {
            $tplIdx = $this->importPage($i);
            $this->AddPage();
            $this->useTemplate($tplIdx);
        }

        unlink($outputFileName);

        $content = $this->Output(null, 'S');

        return $content;
    }

    public function getDocumentsPaths()
    {
        return $this->documentsPaths;
    }

    public function setDocumentsPaths($documentsPaths)
    {
        $this->documentsPaths = $documentsPaths;
    }

    public function addDocumentPath($documentPath)
    {
        $this->documentsPaths[] = $documentPath;
    }
}

The usage of this class is as follows:

$pdf = new MergedPDF();
$pdf->setTitle($pdfTitle);
$pdf->addDocumentPath($absolutePath1);
$pdf->addDocumentPath($absolutePath2);
$pdf->addDocumentPath($absolutePath3);
$tempFileName = tempnam(sys_get_temp_dir(), 'merged');
$content = $pdf->Render();
file_put_contents($tempFileName, $content);

Solution 7 - Php

I have tried similar issue and works fine, try it. It can handle different orientations between PDFs.

    // array to hold list of PDF files to be merged
	$files = array("a.pdf", "b.pdf", "c.pdf");
	$pageCount = 0;
	// initiate FPDI
	$pdf = new FPDI();

	// iterate through the files
	foreach ($files AS $file) {
		// get the page count
		$pageCount = $pdf->setSourceFile($file);
		// iterate through all pages
		for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
			// import a page
			$templateId = $pdf->importPage($pageNo);
			// get the size of the imported page
			$size = $pdf->getTemplateSize($templateId);

			// create a page (landscape or portrait depending on the imported page size)
			if ($size['w'] > $size['h']) {
				$pdf->AddPage('L', array($size['w'], $size['h']));
			} else {
				$pdf->AddPage('P', array($size['w'], $size['h']));
			}

			// use the imported page
			$pdf->useTemplate($templateId);

			$pdf->SetFont('Helvetica');
			$pdf->SetXY(5, 5);
			$pdf->Write(8, 'Generated by FPDI');
		}
	}

Solution 8 - Php

I created an abstraction layer over FPDI (might accommodate other engines). I published it as a Symfony2 bundle depending on a library, and as the library itself.

The bundle

The Library

usage:

public function handlePdfChanges(Document $document, array $formRawData)
{
    $oldPath = $document->getUploadRootDir($this->kernel) . $document->getOldPath();
    $newTmpPath = $document->getFile()->getRealPath();

    switch ($formRawData['insertOptions']['insertPosition']) {
        case PdfInsertType::POSITION_BEGINNING:
            // prepend 
            $newPdf = $this->pdfManager->insert($oldPath, $newTmpPath);
            break;
        case PdfInsertType::POSITION_END: 
            // Append
            $newPdf = $this->pdfManager->append($oldPath, $newTmpPath);
            break;
        case PdfInsertType::POSITION_PAGE: 
            // insert at page n: PdfA={p1; p2; p3}, PdfB={pA; pB; pC} 
            // insert(PdfA, PdfB, 2) will render {p1; pA; pB; pC; p2; p3} 
            $newPdf = $this->pdfManager->insert(
                    $oldPath, $newTmpPath, $formRawData['insertOptions']['pageNumber']
                );
            break;
        case PdfInsertType::POSITION_REPLACE: 
            // does nothing. overrides old file.
            return;
            break;
    }
    $pageCount = $newPdf->getPageCount();
    $newPdf->renderFile($mergedPdfPath = "$newTmpPath.merged");
    $document->setFile(new File($mergedPdfPath, true));
    return $pageCount;
}

Solution 9 - Php

This worked for me on Windows

  1. download PDFtk free from https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/<br />

  2. drop folder (PDFtk) into the root of c:

  3. add the following to your php code where $file1 is the location and name of the first PDF file, $file2 is the location and name of the second and $newfile is the location and name of the destination file

     $file1 = ' c:\\\www\\\folder1\\\folder2\\\file1.pdf';  
     $file2 = ' c:\\\www\\\folder1\\\folder2\\\file2.pdf';  
     $file3 = ' c:\\\www\\\folder1\\\folder2\\\file3.pdf';   
    
     $command = 	'cmd /c C:\\\pdftk\\\bin\\\pdftk.exe '.$file1.$file2.$newfile;
     $result = exec($command);
    

Solution 10 - Php

myokyawhtun's solution worked best for me (using PHP 5.4)

You will still get an error though - I resolved using the following:

Line 269 of fpdf_tpl.php - changed the function parameters to:

function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='',$align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0) { 

I also made this same change on line 898 of fpdf.php

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionImrul.HView Question on Stackoverflow
Solution 1 - PhpSanjeev ChauhanView Answer on Stackoverflow
Solution 2 - PhpAgelessEssenceView Answer on Stackoverflow
Solution 3 - PhpChristaView Answer on Stackoverflow
Solution 4 - PhpSvetoslav GenovView Answer on Stackoverflow
Solution 5 - PhpEaten by a GrueView Answer on Stackoverflow
Solution 6 - PhpArtur KarczmarczykView Answer on Stackoverflow
Solution 7 - PhpKevin ChuiView Answer on Stackoverflow
Solution 8 - PhpjuanmfView Answer on Stackoverflow
Solution 9 - PhpStewart KirkpatrickView Answer on Stackoverflow
Solution 10 - PhpScottView Answer on Stackoverflow