I am developing a web app using Ruby on Rails 3. One of the features of the app is to use data from a MySQL database to fill PDF template forms that have been designed in Adobe LiveCycle Designer.
I am using the technique of generating an XFDF file with the data and use it to fill in the actual PDF file. I am using PDFtk to do this and if I run it from my command prompt (Windows 7 64bit) it works fine.
I used code by Greg Lappen at http://bleep.lapcominc.com/2012/02/07/filling-pdf-forms-with-ruby-and-pdftk/ to implement this process in my Rails app, but it does not seem to work
The output PDF cannot be opened in Acrobat as it states the file has been damaged. If I open it using a normal text editor all it contains is #<StringIO:0x5958f30>
with the HEX value changing after each output.
The code generating the XML data is correct. I was able to save it to a file and run it through the command prompt myself.
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
I suspect the real problem is in either of the two code snippets below. I just started learning Ruby on Rails and I am unable to debug this. I have tried various different methods but no success so far. I would really appreciate any help.
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
This is the full code in my controller class
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
This is the full code for the pdf_stamper class I am using
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
UPDATE #1:
I ran the web app on Ubuntu and I still get the same errors. After digging around on the web I changed the code in my controller to this:
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
I changed StringIO to be in binary write mode and it works in Ubuntu! The PDF opens correctly with all the fields filled in. I opened the same file on Windows using Acrobat and no problems, BUT if I run the web app on Windows, it still produces damaged PDF files.
Does anyone have any solutions on how to get this working in Windows? I am guessing it has something to do with the way Windows and Linux interpret newlines or something similar to that?
After some more searching through the Ruby documentation I managed to solve my problem. Now my app is able to produce valid PDF files on Windows. Here is my solution for anyone that is experiencing the same problem.
The solution was to use IO instead of StringIO in the controller.
My FormPages controller code
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
The pdf_stamper class in charge of filling and generating the PDF using PDFtk
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