Об’єднати PDF-файли з PHP [закрито]


83

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


Пов'язане запитання (відповів до кінця): stackoverflow.com/questions/2713701/…
Fran Verona

3
@Webnet насправді 64% - це нормально. Я б сказав, що 0-25% = невдача, але, мабуть, саме тут це стає суб'єктивним
Шон Патрік Флойд,

Чи можете ви використовувати інструмент командного рядка?
Пекка

Чи можете ви використовувати Zend Framework? stackoverflow.com/questions/4254218/…
Пекка

Де я можу знайти файл "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, який я написав.
Кріста

6
@Christa Остерігайтеся, що FPDI буде аналізувати лише певні файли PDF. Я стикаюся з проблемою, коли FPDI не може аналізувати файли PDF вище v 1.4, і FPDI змушує мене купувати їх парсер для обробки> v1.4 ... yar ....
n0nag0n

Чи не вважаєте ви, що краще зробити $ i = 0 і $ i <= $ countcount. Це покращує читання, я думаю. Чудовий приклад, до речі, справді мені допоміг
Небулосар

123

Нижче наведена команда злиття 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 за замовчуванням, я думаю , мені не довелося його встановлювати.
Вульго Аліас

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

3
В основному це хтось реалізував відповідь @ Christa (FPDF + FDPI), що чудово :) Дякую!
Науель

5
Він також не працює з деякими типами стиснення на деяких PDF-файлах.
Тео Кузеліс,

3
Використовуючи це разом з DOMPDF, це спрацьовує як привабливість дякую
Метью

1
Я отримую "Помилка FPDF: Не вдається знайти таблицю xref." будь-яке рішення для цього?
Sameeraa4ever

1
Це працює, але іноді відображається помилка нижче ... Помилка FPDF: Цей документ (samplepdfs / four.pdf), ймовірно, використовує техніку стиснення, яка не підтримується безкоштовним парсером, що постачається з FPDI.
Nikhil

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

Спрощена версія відповіді Чаухана


Для мене це добре працювало на виділеному сервері hostgator centos, тому ghostscript вже потрібно встановити
Mike

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 Мені це подобається, але це не працює на ландшафті і, здається, об’єднує лише 1-у сторінку.
Geraldo Isaaks

2
@GeraldoIsaaks - згодом я додав підтримку багатосторінкових документів у власному додатку. Я оновив відповідь. Не впевнений у питаннях ландшафту - я не стикався з цим.
billynoah

Що накладено на цей екзамен, який доступний з перших днів ПІІ?
Ян Слабон

@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
Senty

переконайтеся, що у вас правильно налаштовано 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;
}

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
Рей

Для мене це працювало лише так: $command = "cmd /c C:\\pdftk\\bin\\pdftk.exe {$file1} {$file2} cat output {$new}";Зверніть увагу на додатковий вихід кішки . Див. Приклади 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.