新手问题 Ruby 处理 PDF 的开源项目大家有了解么?

evenluo · 2016年03月01日 · 最后由 yakjuly 回复于 2016年03月04日 · 3409 次阅读

最近在做一个与 pdf 处理相关的事情,希望能用 ruby 处理 pdf,做到例如拼合,拆分,在 pdf 上添加图片生成新的 pdf 等功能。不知道各位有没有这方面的相关经验?

用过一个 gem“pdfkit”,不过是用来将 html 页面转为 pdf 文件的,不知道对你有没有帮助。

可以结合 phantomjs 用 html 生成 pdf,样式用 css 控制。

可以看看 gitbook 是用啥做的。

Prawn 足够强大了。

#4 楼 @angelfan 这个不错,谢谢了

Focus Solutions, Inc 做这方面有很多年的经验了

  1. 拆分没用代码歇过,但是这个应该是最简单的功能了,用 pdftk 命令即可。
  2. 合并

pdftk 和 iText 都可以做 merge,甚至 Prawn 也可以做合并,但是 Prawn 最慢,不用考虑。 pdftk 可以直接在系统中装了直接用命令行。 iText 需要 Rjb,但是合并速度更快效率更高。看代码

def self.itext_merge(filenames, output)
  class_pdfreader     = Rjb::import('com.lowagie.text.pdf.PdfReader')
  class_pdfcopyfields = Rjb::import('com.lowagie.text.pdf.PdfCopyFields')
  class_filestream    = Rjb::import('java.io.FileOutputStream')

  filestream = class_filestream.new(output)
  copy = class_pdfcopyfields.new(filestream)
  failure_list = []

  filenames.each do |f|
    # remove double quote
    if match = /"(.*)"/.match(f)
      f = match[1]
    end

    if File.exists?(f)
      begin
        copy.addDocument(class_pdfreader.new(f))
      rescue => e
        failure_list << f
        Rails.logger.info "PdfMerger: Invalid PDF: #{f}"
      end
    else
      failure_list << f
      Rails.logger.info "PdfMerger: File does not exist: #{f}"
    end
  end
  copy.close()
  # pages = class_pdfreader.new(output).getNumberOfPages

  Rails.logger.info "failed pdf: #{failure_list}" if failure_list.present?
  return failure_list.empty?
rescue Exception => e
  Rails.logger.info "itext_merge failure: #{e.message}"
  return false
end

def self.pdftk_merge(filenames, output)
  #pdftk a.pdf b.pdf output c.pdf dont_ask
  response = `pdftk #{filenames.join(" ")} output #{output.to_s} dont_ask`
  Rails.logger.info(response) if response.present?

  not (response =~ /Error/)
end
  1. 生成 PDF

需要看你的需求,2 个方案 wkhtmltopdf 或者 Prawn 区别 Prawn,写代码费劲,需要计算文字大小,格式,预留位置等,支持多重 font-type WkhtmltoPDF,写代码方便,不方便控制字体,宽度,分页等。

以上两个都是从 0 生成 PDF,如果你是要修改 pdf,添加一个图片的话还可以使用 PDF::Stamper, 其实 Stamper 也是使用 iText 的。 你可以自己改下代码。 比如 添加二维码

# barcodes:
#  [
#   {:token => "xsfsdfsdfdsf", :format => "Code128", :page => 1}
#  ]
def self.embed_barcode(input_pdf_path, output_pdf_path = nil, barcodes = [])
  output_pdf_path ||= Rails.root.join("tmp/set_barcode_output_#{rand(10000000)}.pdf")

  stamper = PDF::Stamper.new(input_pdf_path.to_s)
  barcodes.each do |barcode|
    page = barcode[:page]
    page_height = stamper.get_page_height(page)
    page_width = stamper.get_page_width(page)

    img = generate_barcode_img(barcode[:format], barcode[:token])
    stamper.add_image(img, page)
  end
  stamper.save_as(output_pdf_path)
  output_pdf_path
end

#Format: Code128 , DataMatrix
def self.generate_barcode_img(format, token)
  builder = Barby.const_get(format)
  barcode = builder.new(token)
  outputter = Barby::PngOutputter.new(barcode)
  outputter.margin = 0
  img_path = Rails.root.join("tmp/barcode_#{format}_#{token}.png")

  img_size_opts = case format
  when "Code128", "Code128A", "Code128B", "Code128C"
    {:height => 16}
  when "DataMatrix"
    {}
  end
  File.open(img_path, "w") {|f| f.write outputter.to_png(img_size_opts) }
  img_path
end


def add_image(image_path, page, x = nil,y = nil)
  image = Rjb::import('com.lowagie.text.Image')
  img = image.getInstance(image_path)

  img.setAbsolutePosition(x || 550, y || 40);
  #img.scalePercent(20);

  cb = @stamp.getOverContent(page)
  cb.addImage(img)
end

如果你对 PDF 还有更详细需求,可以找我们

需要 登录 后方可回复, 如果你还没有账号请 注册新账号