Слияние PDF-файлов с PHP

Моя концепция такова - на веб-сайте 10 pdf файлов. Пользователь может выбрать несколько файлов PDF, а затем выбрать объединить, чтобы создать один файл PDF, который содержит выбранные страницы. Как я могу сделать это с php?

11 ответов

Решение

Я делал это раньше. У меня был PDF, который я сгенерировал с помощью fpdf, и мне нужно было добавить к нему переменное количество PDF-файлов.

Итак, у меня уже есть объект и страница fpdf (http://www.fpdf.org/), и я использовал fpdi для импорта файлов (http://www.setasign.de/products/pdf-php-solutions/fpdi/) FDPI добавляется расширением класса 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); 
    }

Это в основном превращает каждый PDF в изображение для вставки в ваш другой PDF. Это работало удивительно хорошо для того, для чего я нуждался.

Ниже приведена команда объединения 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);

Я забыл ссылку, откуда я ее нашел, но она отлично работает.

Я предлагаю PDFMerger с github.com, так легко, как:

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

Упрощенная версия ответа Чаухана

Не совсем понятно, почему как принятый ответ, так и даже домашняя страница FDPI, похоже, дают неверные или неполные примеры. Вот мой, который работает и легко реализовать. Как и ожидалось, для этого требуются библиотеки fpdf и 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');

Коды, используемые в PDFMerger, устарели в версии PHP 5. Я раздвоил и исправил коды для работы с PHP 5. Вы можете получить доступ к моей учетной записи на github https://github.com/myokyawhtun/PDFMerger

У меня была похожая проблема в моем программном обеспечении. Мы хотели объединить несколько файлов PDF в один файл PDF и отправить его во внешнюю службу. Мы использовали решение FPDI, как показано в решении Christa.

Тем не менее, используемые нами входные PDF-файлы могут иметь версию выше 1.7. Мы решили оценить коммерческое дополнение FPDI. Однако оказалось, что некоторые документы, отсканированные нашим офисным копировальным аппаратом, имели некорректные индексы, что привело к сбою коммерческого надстройки FPDI. Поэтому мы решили использовать решение Ghostscript, как в ответе Чаухана.

Но затем мы получили некоторые странные метаданные в выходных свойствах PDF.

Наконец, мы решили объединить два решения для объединения и понижения PDF с помощью Ghostscript, но метаданные устанавливаются FPDI. Мы пока не знаем, как это будет работать с некоторыми расширенными форматированными PDF-файлами, но для сканирования мы используем его просто отлично. Вот наша выдержка из класса:

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

Использование этого класса заключается в следующем:

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

Я пробовал подобную проблему и работает нормально, попробуйте. Он может обрабатывать различные ориентации между 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');
        }
    }

Это работало для меня на Windows

  1. скачать PDFtk бесплатно с https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
  2. перетащите папку (PDFtk) в корень c:
  3. добавьте следующее к вашему php-коду, где $file1 - это местоположение и имя первого PDF-файла, $file2 - это местоположение и имя второго, а $newfile - это местоположение и имя конечного файла.

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

Я создал слой абстракции поверх FPDI (может вместить другие движки). Я опубликовал его как пакет Symfony2 в зависимости от библиотеки и как саму библиотеку.

Расслоение

Библиотека

использование:

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

Решение myokyawhtun работает лучше всего для меня (с использованием PHP 5.4)

Вы все равно получите ошибку - я решил, используя следующее:

Строка 269 файла fpdf_tpl.php - изменила параметры функции на:

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

Я также сделал это то же самое изменение в строке 898 fpdf.php

Другие вопросы по тегам