【问题标题】:wicked_pdf multi page documentwicked_pdf 多页文档
【发布时间】:2015-09-07 09:44:36
【问题描述】:

我有一个场景,我需要基于对象数组生成多页 pdf。

以下是我的模型:

# Member has_many certificates

class Member < ActiveRecord::Base

  has_many :certificates, dependent: :destroy

  before_save :set_expiry

  def self.delete_expired
    where(expire_at: Date.today).destroy_all
  end

  private
    def set_expiry
      self.expire_at = 15.days.from_now
    end
end

# certificate belongs_to member
class Certificate < ActiveRecord::Base
  belongs_to :member
  belongs_to :certificate_type
  belongs_to :company

  def company_name
    company.name
  end

  def page_orientation
    company_name == 'CompanyA' ? 'Landscape' : 'Portrait'
  end

  def page_height
    company_name == 'CompanyA' ? '10.5in' : '11in'
  end

  def page_width
    company_name == 'CompanyA' ? '7in' : '8.5in'
  end
end

# each type can have many certificates
class CertificateType < ActiveRecord::Base
  has_many :certificates
end

用户流程如下:

  • 用户上传包含会员记录的 Excel 文件。系统存储记录。
  • 然后用户可以为单个成员生成证书。 (已经开始工作了)。
  • 用户可以为上传的所有成员中的多个成员批量生成证书。

我有一个自定义表单,我在其中创建证书对象(复选框选择),然后提交表单。控制器接收证书对象的散列。我必须一一遍历所有对象以为其关联成员创建证书。问题是每个证书都有自己的模板和一些自定义页面布局设置,如方向、宽度和高度。我想生成一个包含多个页面的单个 pdf 文件,其中每个页面都是一个证书。我被困在我应该在哪里以及如何传递每个证书的页面布局设置上。我在控制器中尝试了 render_to_string 并尝试在那里设置页面布局选项,但它似乎不起作用。我需要一些建议或指点。

【问题讨论】:

  • 是否使用外部实用程序,例如poppler-utils 一个选项(即您是否可以控制服务器)?如果它在 Heroku 或其他东西上,它可能不是。如果是这样,我可以为您详细介绍如何在您的场景中使用它。
  • @fdisk 这不是 Heroku。我正在使用数字海洋,我想应该可以。

标签: ruby-on-rails ruby pdf wicked-pdf


【解决方案1】:

以@fdisk 的回答为基础,但使用 Ruby 原生 combine_pdf gem... 您可以尝试:

require 'combine_pdf'

merged_certificates = CombinePDF.new

@member.certificates.each do |certificate|
  pdf_data = WickedPdf.new.pdf_from_string(render_to_string(something, layout: some_layout))
  merged_certificates  << CombinePDF.parse(pdf_data)
end

send_data merged_certificates.to_pdf, filename: 'mypdf.pdf' #...

【讨论】:

  • merged_certificates &lt;&lt; CombinePDF.new.parse(pdf_data) 应该是 merged_certificates &lt;&lt; CombinePDF.parse(pdf_data)。顺便说一句,您的回答对我帮助很大!谢谢!
  • @NelsonSenna,感谢您的收获 :) 我很高兴这对您有所帮助。
【解决方案2】:

在我的例子中,我想在后台作业中生成多个 PDF,并将最终合并的文件作为附件保存在我的模型中。所以我这样做了:

def generate_and_send
 begin
  final_pdf = pdf_from_html.to_pdf
  FileUtils.mkdir_p(File.dirname(pdf_path)) unless Dir.exists?(File.dirname(pdf_path))
  File.open(pdf_path, 'wb') do |file|
    file << final_pdf
  end
  @object.attachment = File.open pdf_path
  @object.save
 rescue => e
  notifiy_status('error')
 end

我的 pdf 生成器和合并器是这样设置的

   def pdf_from_html
    #Will generate each page in separate to prevent memory overflow
    #Then joins the pages
    final_pdf = CombinePDF.new
    pages = @pages_to_print.select{ |k, v| v == '1'}.keys
    pages.each do |page|
      page_html = render("#{page}") #This method defines the layout based on the page
      pdf_from_page = WickedPdf.new.pdf_from_string(page_html)
      final_pdf << CombinePDF.parse(pdf_from_page, allow_optional_content: true)
    end
    final_pdf
  end

提示:记得使用方法.to_pdf

【讨论】:

    【解决方案3】:

    由于这是一个选项,我建议通过发行版的包管理器(yumapt-getdnf 等)安装 poppler-utils。之后,将每个证书保存为Tempfile

    require 'shellwords'
    
    certificates = @member.certificates.map do |certificate|
      Tempfile.open(["cert", ".pdf"]) { |file| file << WickedPdf.new.pdf_from_string(render_to_string(something, layout: some_layout)) }
    end
    
    paths = certificates.map { |cert| cert.path.shellescape }
    
    @final_pdf = Tempfile.new(["cert", ".pdf"])
    @final_pdf.close
    %x[pdfunite #{paths} #{@final_pdf.path.shellescape}]
    

    @final_pdf 对象应包含合并的单个证书。我还制作了一个简单的gem 来处理这些情况,但它可能会产生一些您不想要的额外开销(它旨在创建带有嵌套书签的组合 PDF 文件)。

    【讨论】:

    • 您可能也可以使用原生 Ruby 解决方案,例如 CombinePDF
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-29
    • 1970-01-01
    相关资源
    最近更新 更多