Объединение файлов PDF с PHP [закрыто]

83

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

Imrul.H
источник
Связанный вопрос (ответ кстати): stackoverflow.com/questions/2713701/…
Fran Verona
3
@Webnet на самом деле 64% нормально. Я бы сказал, что 0-25% = неудача, но я полагаю, что именно здесь это становится субъективным
Шон Патрик Флойд
Можете ли вы использовать инструмент командной строки?
Pekka
Можете ли вы использовать Zend Framework? stackoverflow.com/questions/4254218/…
Pekka
Где найти файл "pdftk-112-1i386.rpm" и как его установить на сервер?
Imrul.H

Ответы:

28

Я делал это раньше. У меня был 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-файл. Это сработало на удивление хорошо для того, для чего мне это было нужно.

Криста
источник
Я не могу понять ваш код. не могли бы вы объяснить более подробно? Я также не нашел функций "setSourceFile" и "importPage" в руководстве по fpdf.
Imrul.H
Я вернулся и более подробно рассмотрел свое решение. Надеюсь, это будет более полезно. Я полностью забыл о части fdpi сегодня утром, когда писал это, это небольшая часть довольно сложного генератора PDF, который я написал.
Christa
7
@Christa Помните, что FPDI будет анализировать только определенные файлы PDF. Я столкнулся с проблемой, когда FPDI не будет анализировать файлы PDF выше версии 1.4, а FPDI заставляет меня покупать их парсер для обработки> v1.4 ... yar ....
n0nag0n 06
Вам не кажется, что лучше сделать $ i = 0 и $ i <= $ pagecount. Думаю, от этого лучше читать. Замечательный пример, кстати, действительно помог мне
Nebulosar
126

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

Я забыл ссылку, откуда нашел, но работает нормально.

Примечание. У вас должен быть установлен gs (в Linux и, возможно, Mac) или Ghostscript (в Windows), чтобы это работало.

Санджив Чаухан
источник
4
У меня он работал без проблем и без установки внешних библиотек, таких как FPDI или другие.
Memochipan
4
Это решение сработало для меня лучше всего. Установить Ghostscript на мой сервер было очень просто. Это было просто «yum install ghostscript». И ваш сценарий отлично сработал
Тео Кузелис
1
У меня пустая страница pdf :(
itsazzad 02
2
Вам необходимо установить Ghostscript, иначе он выйдет из строя.
Паскаль Кляйн
2
Вы должны объяснить, что он на самом деле делает. На самом деле это не способ решения задачи с помощью php, в php вы только подготавливаете данные, а затем выполняете сценарий оболочки, который выполняет фактическую задачу. также вы должны включить в свой ответ, что gs (в Linux и, возможно, Mac) или Ghostscript (в Windows) должны быть установлены, чтобы это работало .. Тем не менее мне очень нравится это решение, поскольку gs по умолчанию включен в Ubuntu, я думаю , мне не пришлось его устанавливать.
Vulgo Alias
39

Я предлагаю 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
AgelessEssence
источник
3
Это в основном чья-то реализация ответа @ Christa (FPDF + FDPI), и это здорово :) Спасибо!
Науэль
5
Он также не работает с некоторыми типами сжатия некоторых PDF-файлов.
Тео Кузелис
3
Используя это с DOMPDF, и это прекрасно работает, спасибо!
Мэтью
1
Я получаю сообщение «Ошибка FPDF: не удается найти таблицу внешних ссылок». любое решение для этого?
Sameeraa4ever
1
Он работает, но иногда показывает ошибку ниже ... Ошибка FPDF: в этом документе (samplepdfs / four.pdf), вероятно, используется метод сжатия, который не поддерживается бесплатным парсером, поставляемым с FPDI.
Nikhil
12
$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=".$new." ".implode(" ", $files);
shell_exec($cmd);

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

Святослав Генов
источник
Это отлично сработало для меня на выделенном сервере hostgator centos, поэтому ghostscript уже должен быть установлен
Майк Волмар
Должен быть отмечен как правильный ответ, потому что это было чертовски ЛЕГКО. Часы борются с FPDF и FPDI не загружаются правильно. Вы спасли мою жизнь. Просто нужно установить «imagemagick» (в debian / ubuntu: apt install imagemagick) на сервере, и все в порядке. Ура!
Флориан Дойен,
9

И принятый ответ, и даже домашняя страница 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');
Биллиноа
источник
Привет @billynoah, мне это нравится, но он не работает в альбомной ориентации и, кажется, объединяет только первые страницы.
Джеральдо Исаакс
2
@GeraldoIsaaks - впоследствии я добавил поддержку многостраничных документов в собственное приложение. Я обновил ответ. Не уверен в проблемах с ландшафтом - я не сталкивался с этим.
billynoah
Что плохого в этом экзамене, который доступен с первых дней FPDI?
Ян Слабон
@Setasign - я никогда этого не видел, но спасибо, что поделился.
billynoah
@billynoah Спасибо за ясный и обновленный пример простого кода здесь, в SO. Это заставило меня начать. В итоге я использовал больше кода из примера setasign ( setasign.com/products/fpdi/demos/concatenate-fake , ссылку в комментарии выше легко пропустить). Их логика внутри вызова addPage улучшила внешний вид моих отдельных связанных страниц. Вероятно, также лучше справляется с портретом / пейзажем, хотя я этого не тестировал. Но я не нашел примера с поисками и не знал, что мне это интересно, пока не увидел ваш ответ.
Энн Ганн
5

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

Однако входные 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);
Артур Карчмарчик
источник
Просто упомяну, что я использовал тот же код в Windows env. и не забудьте поместить папку с программой в ", но не параметры".$cmd = "\"C:\\Program Files\\gs\\gs9.20\\bin\\gswin64c.exe\" -q -dNOPAUSE -dBATCH -dCompatibilityLevel=1.4 -sDEVICE=pdfwrite -sOutputFile=[....your parameters...]" ;
Фредерик Клее
3

Я пробовал аналогичную проблему и отлично работает, попробуйте. Он может обрабатывать разные ориентации 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');
        }
    }
Кевин Чуи
источник
Это даетUndefined index: w
seny
убедитесь, что у вас правильно настроен FPDF
Кевин Чуи
параметры для меня были $ size ['width'] и $ size ['height'] вместо $ size ['w'] и $ size ['h']
gorillagoat
0

Я создал уровень абстракции поверх 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;
}
juanmf
источник
0

Это сработало для меня в 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);
    
Стюарт Киркпатрик
источник
Есть оболочка PHP, которая делает это намного чище. См. Github.com/mikehaertl/php-pdftk
Шон Бин
Примечание: PdfTK не работает с RHEL 7 или Cent OS 7
Ray
Для меня это работало только так: $command = "cmd /c C:\\pdftk\\bin\\pdftk.exe {$file1} {$file2} cat output {$new}";обратите внимание на дополнительный вывод cat . См. Примеры PDFtk
maxpower9000,
-1

Решение 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.

Скотт
источник