无法使用PDFtk生成正确的PDF文件

时间:2013-07-03 17:54:22

标签: ruby-on-rails ruby pdftk

我正在使用Ruby on Rails 3开发一个Web应用程序。该应用程序的一个功能是使用来自MySQL数据库的数据来填充在Adobe LiveCycle Designer中设计的PDF模板表单。

我正在使用生成带有数据的XFDF文件的技术,并使用它来填充实际的PDF文件。我使用PDFtk来执行此操作,如果我从命令提示符(Windows 7 64位)运行它,它可以正常工作。

我在http://bleep.lapcominc.com/2012/02/07/filling-pdf-forms-with-ruby-and-pdftk/使用Greg Lappen的代码在我的Rails应用程序中实现此过程,但它似乎不起作用

无法在Acrobat中打开输出PDF,因为它表明文件已损坏。如果我使用普通文本编辑器打开它,它包含#<StringIO:0x5958f30>,每次输出后HEX值都会改变。

生成XML数据的代码是正确的。我能够将它保存到文件中并通过命令提示符自行运行。

def self.generate_xfdf(fields, filename)
    xml = Builder::XmlMarkup.new
    xml.instruct!
    xml.xfdf("xmlns" => "http://ns.adobe.com/xfdf/", "xml:space" => "preserve") {
      xml.f :href => filename
      xml.fields {
        fields.each do |field, value|
          xml.field(:name => field) {
            if value.is_a? Array
              value.each {|item| xml.value(item.to_s) }
            else
              xml.value(value.to_s)
            end
          }
        end
      }
    }
    xml.target!
  end

我怀疑真正的问题出在下面两个代码段中的任何一个。我刚开始学习Ruby on Rails,我无法调试它。我尝试了各种不同的方法但到目前为止没有成功。我真的很感激任何帮助。

  def self.stamp(input_pdf, fields)
    stdin, stdout, stderr = Open3.popen3("pdftk #{input_pdf} fill_form - output - flatten")
    stdin << generate_xfdf(fields, File.basename(input_pdf))     
    stdin.close
    yield stdout
    stdout.close
    stderr.close
  end

PdfStamper.stamp('C:/clean-it-template.pdf', { 'LastName' => "Test Last Name", 'FirstName' => "Test First Name" }) do |pdf_io|
    pdf_content = StringIO.new
    pdf_content << pdf_io.read
    send_data pdf_content.string, :filename=>'output.pdf', :disposition=>'inline', :type=>'application/pdf'
end

这是我的控制器类中的完整代码

require 'pdf_stamper'

class FormPagesController < ApplicationController
    def pdftest
        PdfStamper.stamp('C:/clean-it-template.pdf', { 'LastName' => "Test Last Name", 'FirstName' => "Test First Name" }) do |pdf_io|
            pdf_content = StringIO.new
            pdf_content << pdf_io.read
            send_data pdf_content.string, :filename=>'output.pdf', :disposition=>'inline', :type=>'application/pdf'
        end
    end
end

这是我正在使用的pdf_stamper类的完整代码

require 'builder'
require 'open3'

class PdfStamper
  def self.stamp(input_pdf, fields)
    stdin, stdout, stderr = Open3.popen3("pdftk #{input_pdf} fill_form - output - flatten")
    stdin << generate_xfdf(fields, File.basename(input_pdf))     
    stdin.close
    yield stdout
    stdout.close
    stderr.close
  end

  def self.generate_xfdf(fields, filename)

    xml = Builder::XmlMarkup.new
    xml.instruct!
    xml.xfdf("xmlns" => "http://ns.adobe.com/xfdf/", "xml:space" => "preserve") {
      xml.f :href => filename
      xml.fields {
        fields.each do |field, value|
          xml.field(:name => field) {
            if value.is_a? Array
              value.each {|item| xml.value(item.to_s) }
            else
              xml.value(value.to_s)
            end
          }
        end
      }
    }
    xml.target!
    #file = File.new("C:/debug.xml", "w+")
    #file.write(xml_data)
    #file.close
  end

end

更新#1:

我在Ubuntu上运行了网络应用程序但仍然遇到了同样的错误。在网上挖掘后,我将控制器中的代码更改为:

def pdftest
    PdfStamper.stamp('/home/nikolaos/clean-it-template.pdf', { 'LastName' => "Test Last Name", 'FirstName' => "Test First Name" }) do |pdf_io|
        pdf_content = StringIO.new("", 'wb')
        pdf_content << pdf_io.read
        send_data pdf_content.string, :filename=>'output.pdf', :disposition=>'inline', :type=>'application/pdf'
    end
end

我将StringIO更改为二进制写入模式,它可以在Ubuntu中运行! PDF正确打开,填写了所有字段。我使用Acrobat在Windows上打开了相同的文件,没有任何问题,但如果我在Windows上运行Web应用程序,它仍然会生成损坏的PDF文件。

有没有人有任何关于如何在Windows中使用它的解决方案?我猜它与Windows和Linux解释换行符的方式有什么关系?

1 个答案:

答案 0 :(得分:0)

经过一些搜索Ruby文档后,我设法解决了我的问题。现在我的应用程序能够在Windows上生成有效的PDF文件。对于遇到同样问题的人来说,这是我的解决方案。

解决方案是在控制器中使用IO而不是StringIO。

我的FormPages控制器代码

require 'pdf_stamper'

class FormPagesController < ApplicationController
    def pdftest
        PdfStamper.stamp('C:/clean-it-template.pdf', { 'LastName' => "Bukas", 'FirstName' => "Nikolaos" }) do |pdf_io|
            pdf_content = IO.new(pdf_io.to_i, "r+b")
            pdf_content.binmode
            send_data pdf_content.read, :filename=>'output.pdf', :disposition=>'inline', :type=>'application/pdf'
        end
    end
end

pdf_stamper类负责使用PDFtk填充和生成PDF

require 'builder'
require 'open3'

class PdfStamper
  def self.stamp(input_pdf, fields)
    Open3.popen3("pdftk #{input_pdf} fill_form - output -") do |stdin,  stdout, stderr|
      stdin << generate_xfdf(fields, File.basename(input_pdf))
      stdin.close
      yield stdout
      stdout.close
      stderr.close
    end
  end

  def self.generate_xfdf(fields, filename)

    xml = Builder::XmlMarkup.new
    xml.instruct!
    xml.xfdf("xmlns" => "http://ns.adobe.com/xfdf/", "xml:space" => "preserve") {
      xml.f :href => filename
      xml.fields {
        fields.each do |field, value|
          xml.field(:name => field) {
            if value.is_a? Array
              value.each {|item| xml.value(item.to_s) }
            else
              xml.value(value.to_s)
            end
          }
        end
      }
    }
    xml.target!
  end
end