用PHP合并PDF文件[关闭]


83

我的概念是-网站中有10个pdf文件。用户可以选择一些pdf文件,然后选择合并以创建一个包含所选页面的pdf文件。我该如何用PHP做到这一点?


相关的问题(顺便说一句回答):stackoverflow.com/questions/2713701/...
弗兰维罗纳

3
@Webnet实际上,64%可以。我想说0 - 25%=失败,但我想这是它得到的主观
肖恩·帕特里克·弗洛伊德

可以使用命令行工具吗?
Pekka

您可以使用Zend Framework吗?stackoverflow.com/questions/4254218/…–
Pekka

在哪里可以找到“ pdftk-112-1i386.rpm”文件,以及如何将其安装到服务器?
Imrul.H 2011年

Answers:


28

我以前做过 我有一个用fpdf生成的pdf,我需要在其中添加可变数量的PDF。

因此,我已经设置了fpdf对象和页面(http://www.fpdf.org/),并使用fpdi导入了文件(http://www.setasign.de/products/pdf-php-solutions/ fpdi /)通过扩展PDF类来添加FDPI:

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。对于我需要的它,它的工作非常出色。


我无法理解您的代码。你能解释更多细节吗?我也没有在fpdf手册中找到“ setSourceFile”和“ importPage”函数。
Imrul.H 2011年

我回过头来,更详细地研究了我的解决方案。我希望这会有所帮助。今天早晨,当我写这篇文章时,我完全忘记了fdpi部分,这只是我编写的一个非常复杂的PDF生成器的一小部分。
Christa

6
@Christa当心FPDI将仅解析某些PDF文件。我遇到了一个问题,其中FPDI无法解析高于v 1.4的PDF文件,而FPDI正在让我购买其解析器来处理> v1.4 ... yar ....
n0nag0n 2012年

您是否不认为执行$ i = 0且$ i <= $ pagecount更好。我认为阅读起来会更好。顺便说一句很好的例子,确实帮了我
Nebulosar

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”。您的脚本运行完美
Theo Kouzelis 2013年

1
我正在得到一个空白的pdf页面:(
itsazzad 2014年

2
您需要安装Ghostscript,否则它会自动失败。
Pascal Klein

2
您应该解释它的实际作用。实际上,这实际上不是执行任务的php方法,在php中,您仅准备数据,然后执行执行实际任务的shell脚本。您还应该在答案中包括应该安装gs(在Linux上以及可能在Mac上)或Ghostscript(在Windows上)以使其正常工作。 ,我不必安装它。
Vulgo Alias '18

39

我建议PDFMergergithub.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)的实现,太好了:)谢谢!
Nahuel

5
它也不适用于某些PDF的某些类型的压缩。
Theo Kouzelis

3
将此与DOMPDF一起使用,效果非常不错,谢谢!
马修

1
我收到“ FPDF错误:无法找到外部参照表”。有什么解决办法吗?
Sameeraa4ever 2015年

1
它可以工作,但有时会在下面显示错误... FPDF错误:该文档(samplepdfs / four.pdf)可能使用了FPDI随附的免费解析器不支持的压缩技术。
Nikhil

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

Chauhan答案的简化版本


这在hostgator centos专用服务器上对我来说效果很好,因此必须已经安装了ghostscript
Mike Volmar,

9

既被接受的答案,甚至FDPI主页似乎都给出了拙劣的或不完整的示例。这是可行且易于实现的矿井。不出所料,它需要fpdf和fpdi库:

  • FPDF:http://www.fpdf.org/en/download.php
  • FPDI:https://www.setasign.com/products/fpdi/downloads
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我喜欢这个,但是它在横向上不起作用,似乎只合并了第一页。
Geraldo Isaaks '16

2
@GeraldoIsaaks-随后,我在自己的应用程序中添加了对多页文档的支持。我已经更新了答案。不确定景观问题-我还没有遇到过。
billynoah 16/09/22

什么是butched在examle这是可用,因为FPDI初期?
Jan Slabon '16

@Setasign-我从未见过,但感谢您的分享。
billynoah's

@billynoah感谢您在SO中清晰,更新的简单代码示例。它让我开始。我最终使用了setasign示例中的更多代码(setasign.com/products/fpdi/demos/concatenate-fake,在上面的评论中很容易错过链接)。他们在addPage调用中的逻辑使我的特定串联页面看起来更好。尽管我没有测试过,但也许还能更好地处理人像/风景。但是直到找到答案后,我才找到搜索示例,并且不知道自己有兴趣。
Anne Gunn

5

我的软件中也有类似的问题。我们希望将多个PDF文件合并为一个PDF文件,然后将其提交给外部服务。正如Christa解决方案中所示,我们一直在使用FPDI解决方案。

但是,我们一直在使用的输入PDF版本可能高于1.7。我们已经决定评估FPDI商业插件。但是,事实证明,我们的办公室复印机扫描的某些文档的索引格式错误,从而导致商业FPDI加载项崩溃。因此,我们决定在Chauhan的答案中使用Ghostscript解决方案。

但是,然后在输出PDF属性中得到了一些奇怪的元数据。

最后,我们决定加入两种解决方案,以通过Ghostscript合并和降级PDF,但是元数据由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...]" ;
FrédéricKlee,2016年

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
感性的

确保正确配置了FPDF
Cui Chui

对我来说,参数是$ 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. 可从https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/免费下载PDFtk
  2. 将文件夹(PDFtk)放入c的根目录:
  3. 将以下内容添加到您的php代码中,其中$ file1是第一个PDF文件的位置和名称,$ file2是第二个PDF文件的位置和名称,而$ 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 '16

-1

myokyawhtun的解决方案最适合我(使用PHP 5.4)

您仍然会收到错误-我使用以下方法解决了:

fpdf_tpl.php的第269行-将函数参数更改为:

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

我也在fpdf.php的898行进行了相同的更改

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.