web-dev-qa-db-fra.com

Fusionnez les fichiers PDF avec PHP

Mon concept est - il y a 10 fichiers pdf dans un site Web. L'utilisateur peut sélectionner des fichiers pdf, puis fusionner pour créer un seul fichier pdf contenant les pages sélectionnées. Comment puis-je faire cela avec php?

70
Imrul.H

J'ai déjà fait ça avant. J'avais généré un fichier PDF avec fpdf et je devais y ajouter une quantité variable de PDF.

Donc, j'avais déjà un objet fpdf et une page configurée (http://www.fpdf.org/). Et j'ai utilisé fpdi pour importer les fichiers (http://www.setasign.de/products/pdf-php-solutions/ fpdi /) FDPI est ajouté en étendant la classe PDF:

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); 
    }

Cela rend fondamentalement chaque pdf en une image à mettre dans votre autre pdf. Cela a fonctionné étonnamment bien pour ce dont j'avais besoin.

22
Christa

Ci-dessous, la commande de fusion php PDF).

$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);

J'ai oublié le lien d'où je l'ai trouvé, mais cela fonctionne bien.

104
Sanjeev Chauhan

je suggère PDFMerger de github.com , si facile comme ::

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
33
AgelessEssence
$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=".$new." ".implode(" ", $files);
Shell_exec($cmd);

Une version simplifiée de la réponse de Chauhan

11
Svetoslav Genov

Les codes utilisant PDFMerger sont obsolètes en version PHP5. J'ai créé et corrigé les codes avec lesquels travailler PHP 5. Vous pouvez vous connecter à mon compte github https://github.com/myokyawhtun/PDFMerger

9
myokyawhtun

La réponse acceptée et même la page d'accueil de FDPI semblent donner des exemples bâclés ou incomplets. Voici le mien qui fonctionne et est facile à mettre en œuvre. Comme prévu, il nécessite les bibliothèques fpdf et fpdi:

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');
6
billynoah

J'ai eu un problème similaire dans mon logiciel. Nous voulions fusionner plusieurs fichiers PDF en un seul PDF et les envoyer à un service externe. Nous utilisons la solution FPDI comme indiqué dans Christa la solution.

Cependant, les PDF d'entrée que nous avons utilisés pourraient être dans une version supérieure à 1.7. Nous avons décidé d'évaluer l'add-on commercial FPDI. Cependant, il s'est avéré que certains des documents numérisés par notre copieur de bureau présentaient des index malformés, ce qui bloquait le module complémentaire FPDI commercial. Nous avons donc décidé d'utiliser la solution Ghostscript comme dans la réponse de Chauhan.

Mais nous avons eu d’étranges métadonnées dans les propriétés de sortie PDF.

Enfin, nous avons décidé de joindre deux solutions pour obtenir les fichiers PDF fusionnés et rétrogradés par Ghostscript, mais les métadonnées sont définies par FPDI. Nous ne savons pas encore comment cela fonctionnerait avec certains fichiers PDF formatés avancés, mais pour les analyses que nous utilisons, cela fonctionne très bien. Voici notre extrait de classe:

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;
    }
}

L'utilisation de cette classe est la suivante:

$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);
5
Artur Karczmarczyk

J'ai essayé le même problème et fonctionne bien, essayez-le. Il peut gérer différentes orientations entre les PDF.

    // 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');
        }
    }
3
Kevin Chui

Cela a fonctionné pour moi sous Windows

  1. téléchargez PDFtk gratuitement à partir de https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
  2. déposer le dossier (PDFtk) à la racine de c:
  3. ajoutez ce qui suit à votre code php où $ fichier1 correspond à l'emplacement et au nom du premier PDF, $ fichier2 correspond à l'emplacement et au nom du deuxième et $ newfile à l'emplacement et au nom du fichier de destination

    $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);
    
1
Stewart Kirkpatrick

J'ai créé une couche d'abstraction sur FPDI (peut accueillir d'autres moteurs). Je l'ai publié en tant que bundle Symfony2 en fonction d'une bibliothèque et en tant que bibliothèque elle-même.

Le paquet

La bibliothèque

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;
}
0
juanmf

la solution de myokyawhtun fonctionnait mieux pour moi (avec PHP 5.4))

Vous obtiendrez quand même une erreur - j'ai résolu le problème suivant:

Ligne 269 de fpdf_tpl.php - a modifié les paramètres de la fonction en:

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) { 

J'ai également apporté ce même changement à la ligne 898 de fpdf.php

0
Scott