【问题标题】:How do you combine PDFs in ruby?如何在 ruby​​ 中合并 PDF?
【发布时间】:2011-03-30 19:09:39
【问题描述】:

这是asked in 2008。希望现在有更好的答案。

如何在 ruby​​ 中合并 PDF?

我正在使用pdf-stamper gem 在 PDF 中填写表格。我想获取 n 个 PDF,在每个 PDF 中填写一个表格,然后将结果保存为 n 页文档。

你能用像虾这样的原生库做到这一点吗?你能用 rjb 和 iText 做到这一点吗? pdf-stamper 是 iText 的包装器。

如果可能,我想避免使用两个库(即 pdftk 和 iText)。

【问题讨论】:

标签: ruby pdf pdf-generation itext pdftk


【解决方案1】:

如果您想使用 combine_pdf gem 添加任何模板(由 macOS Pages 或 Google Docs 创建),那么您可以尝试这样做:

final_pdf = CombinePDF.new
company_template = CombinePDF.load(template_file.pdf).pages[0]
pdf = CombinePDF.load (content_file.pdf)
pdf.pages.each {|page| final_pdf << (company_template << page)} 
final_pdf.save "final_document.pdf"

【讨论】:

    【解决方案2】:

    在长期寻找纯 Ruby 解决方案后,我最终从头开始编写代码来解析和合并/合并 PDF 文件。

    (我觉得当前的工具太乱了——我想要一些 native 的东西,但它们似乎都有不同的问题和依赖关系......甚至 Prawn 也放弃了他们使用的模板支持)

    我发布了 gem online,您也可以在 GitHub 找到它。

    你可以安装它:

    gem install combine_pdf
    

    它非常易于使用(无论是否将 PDF 数据保存到文件)。

    例如,这里是一个“单行”:

    (CombinePDF.load("file1.pdf") << CombinePDF.load("file2.pdf") << CombinePDF.load("file3.pdf")).save("out.pdf")
    

    如果您发现任何问题,请告诉我,我会努力解决。

    【讨论】:

    • 我可以使用combine_pdf 将多个不同大小的 pdf 合并为一个多页的 pdf,例如将 8 个 pdf 合并为一个 2 页的新 pdf?
    • 我尝试了不同的页面大小,它可以毫无问题地合并 PDF 文件。原始页面大小保持不变。我不确定您所说的合并 8 个文件并获得 2 个页面是什么意思 - 我假设您的意思是 2 个页面大小......?
    • 例如,我的意思是将 2 个 A5 大小的 PDF 合并为 1 个 A4 大小的 PDF。
    • 您好 Tim,CombinePDF 不支持该级别的编辑。它只是为了满足简单操作的需​​要。如果您知道如何使用 CombinePDF 的代码库实现这样的功能,请随时在 github 上打开一个拉取请求/问题,我们会解决的。
    • 我明白了,我需要它来用于即将到来的项目,但我想以图像和虾为例会更容易。但我依赖第三方的内容,所以如果 PDF 是唯一的可能性,那绝对是一种选择。感谢您的回复。
    【解决方案3】:

    在 Ruby 中还没有看到很好的选择 - 我得到了最好的结果,转至 pdftk

    system "pdftk #{file_1} multistamp #{file_2} output #{file_combined}"
    

    【讨论】:

      【解决方案4】:

      从 2013 年起,您可以使用 Prawn 合并 pdf。要点:https://gist.github.com/4512859

      class PdfMerger
      
        def merge(pdf_paths, destination)
      
          first_pdf_path = pdf_paths.delete_at(0)
      
          Prawn::Document.generate(destination, :template => first_pdf_path) do |pdf|
      
            pdf_paths.each do |pdf_path|
              pdf.go_to_page(pdf.page_count)
      
              template_page_count = count_pdf_pages(pdf_path)
              (1..template_page_count).each do |template_page_number|
                pdf.start_new_page(:template => pdf_path, :template_page => template_page_number)
              end
            end
      
          end
      
        end
      
        private
      
        def count_pdf_pages(pdf_file_path)
          pdf = Prawn::Document.new(:template => pdf_file_path)
          pdf.page_count
        end
      
      end
      

      【讨论】:

      • 谢谢。节省大量时间。可以替换以前使用 Java 的 pdf-merger gem。呸。这应该是公认的答案。
      • 我已使用此脚本将数千个 PDF 合并为一个。谢谢!
      • 请注意,Prawn 模板不适用于所有 PDF - 这是一个已知问题,他们已经完全考虑了 dropping support for it。到目前为止,尽管它仍然是最好的 Ruby 解决方案。
      • 只是给找到这个答案的每个人的提示 - 他们现在已经正式删除了模板。您必须回到 0.14.0 版本才能恢复它们。
      • 它不会工作,因为 Prawn 放弃了模板支持。在此处查看更多信息:github.com/prawnpdf/prawn/issues/376
      【解决方案5】:

      使用 ghostscript 合并 PDF:

       options = "-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite"
       system "gs #{options} -sOutputFile=result.pdf file1.pdf file2.pdf"
      

      【讨论】:

        【解决方案6】:

        我为此编写了一个 ruby​​ gem ——PDF::Merger。它使用 iText。以下是您的使用方法:

        pdf = PDF::Merger.new
        pdf.add_file "foo.pdf"
        pdf.add_file "bar.pdf"
        pdf.save_as "combined.pdf"
        

        【讨论】:

        • 我对 iText 许可证很好奇。如果您有一个 Rails 应用程序,您是否必须购买许可证,或者您可以在不开源整个应用程序的情况下免费使用它吗?
        • iText = 5.0 是 Affero GPL。 pdf-merger 使用 4.2。
        • 我可以从亚马逊存储桶中获取远程 pdf 并将其与您的 gem 合并吗?
        • gem 仅适用于本地文件系统中的文件。如果您安装了 S3 存储桶(例如,使用 S3FS),那么可以确定。否则,不行,你需要先下载它。
        • 如果您想避免仅为这个 gem 安装 JVM,请查看 Evan Closson 的解决方案。
        【解决方案7】:

        通过 iText,这将起作用...尽管您应该在合并表单之前将它们展平以避免字段名称冲突。或者一次一页重命名字段。

        在 PDF 中,具有相同名称的字段共享一个值。这通常不是理想的行为,尽管它有时会派上用场。

        类似于(在 java 中):

        PdfCopy mergedPDF = new PdfCopy( new Document(), new FileOutputStream( outPath );
        
        for (String path : paths ) {
          PdfReader reader = new PdfReader( path );
          ByteArrayOutputStream curFormOut = new ByteArrayOutputStream();
          PdfStamper stamper = new PdfStamper( reader, curFormOut );
        
          stamper.setField( name, value ); // ad nauseum
        
          stamper.setFlattening(true); // flattening setting only takes effect during close()
          stamper.close();
        
          byte curFormBytes = curFormOut.toByteArray();
          PdfReader combineMe = new PdfReader( curFormBytes );
        
          int pages = combineMe .getNumberOfPages();
          for (int i = 1; i <= pages; ++i) { // "1" is the first page
            mergedForms.addPage( mergedForms.getImportedPage( combineMe, i );
          }
        }
        
        mergedForms.close();
        

        【讨论】:

        • 有一个更简单的方法来做到这一点 - 你可以使用 PdfCopyFields 和 addDocument。看看我制作的宝石。
        • 当然,但 PdfCopyFields 不会重命名字段......并且鉴于“同名 == 相同值”的事情,我认为展平是最好的路线。我认为字段重命名就在 CopyField 的小巷中,但我在 API 参考中看不到任何内容:api.itextpdf.com。 PdfStamper 可以更改字段名称,但不会为您处理导入。可悲的是,iText 经常遇到这种“不能走路和嚼口香糖”类型的问题,要求您创建、“保存”并阅读相同的 PDF 以将其应用于其他事情。效率不是很高,但它确实有效,而且价格难以承受。
        【解决方案8】:

        我们比 2008 年更接近了,但还没有完全达到。

        Prawn 的最新开发版允许您使用现有的 PDF 作为模板,但在添加更多页面时不能反复使用模板。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-07-25
          • 2011-02-12
          • 2022-01-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-11
          相关资源
          最近更新 更多