How to Generate PDF in Ruby on Rails (HowtoGeneratePDFs) ZT

Source: Internet
Author: User
Tags fdf fdf file pdflib
Document directory
  • Headers for Internet Explorer
  • Do not use a layout
This howto covers seven approaches to generating a PDF document with Rails.

  • HTMLDOC
  • PdfWriter
  • PDF: Writer (Austin Ziegler)
  • Ruby FPDF
  • JasperReports
  • PDF Form Fill
  • PDFlib and PDFlib-Lite
  • Rfpdf
Using HTMLDOC

The sample code below requires HTMLDOC.

  #in controller
def pdf
@article = Article.find(@params["id"])
add_variables_to_assigns

generator = IO.popen("htmldoc -t pdf --path \".;http://#{@request.env["HTTP_HOST"]}\" --webpage -", "w+")
generator.puts @template.render("article/pdf")
generator.close_write

send_data(generator.read, :filename => "#{@article.title}.pdf", :type => "application/pdf")
end

If you're using Windows, you may have problems unless you add the following after generator = IO. popen ....

generator.binmode
PdfWriter

Alternatively
If you want more control over where everything is written/drawn
The page James Hollingshead has put some code up at http://www.hollo.org/pdfwriter
. It also generates everything in a single pass so no need
Temporary files. It is very lightweight and all in a single file that
Can be copied to the lib directory and required. Using it is as simple
As:

  #in controller
def pdf
send_data gen_pdf, :filename => "something.pdf", :type => "application/pdf"
end

private
def gen_pdf
pdf = PdfWriter.new
pdf.newPage
pdf.writeText(10, 200, 'Text to write', :fontsize => 18)
pdf.writeLine(0, 0, 100, 100) #Draw line
pdf.newPage
pdf.writeText(10, 210, 'Now on page 2')
pdf.writeEnd
end
PDF: Writer (Austin Ziegler)

Http://rubyforge.org/projects/ruby-pdf/

(Instructions updated by Austin Ziegler .)

Install PDF: Writer (and dependencies) with \ RubyGems:

gem install pdf-writer

One way is to create a. pdf file in public/pdf and send it to the browser with a redirect, as shown below:

  #in controller
require 'pdf/writer'

def pdf
gen_pdf
redirect_to("#{@request.relative_url_root}/pdf/hello.pdf")
end

private
def gen_pdf
pdf = PDF::Writer.new
pdf.select_font "Times-Roman"
pdf.text "Hello, Ruby.", :font_size => 72, :justification => :center

pdf.save_as("public/pdf/hello.pdf")
end

Alternately, generate the document and send it directly to the browser:

 # in controller
require 'pdf/writer'

def pdf
_p = PDF::Writer.new
_p.select_font 'Times-Roman'
_p.text "Hello, Ruby.", :font_size => 72, :justification => :center
send_data _p.render, :filename => filename, :type => "application/pdf"
end

This
Is the preferred way to send documents, as the documents will be sent
Inline and two requests won't step on each others 'generated documents.
There will be further details on what is possible in an upcoming Ruby
Code & Style article that I'm writing.

Another alternative method is to create a template handler to handle, say,rpdfFiles:


ActionView::Base.register_template_handler 'rpdf', ActionView::PDFRender

In yourconfig/environment.rbFile, and put the following somewhere inlibDirectory:


module ActionView # :nodoc:
class PDFRender
PAPER = 'A4'
include ApplicationHelper

def initialize(action_view)

@action_view = action_view
end

# Render the PDF
def render(template, local_assigns = {})
@action_view.controller.headers["Content-Type"] ||= 'application/pdf'

# Retrieve controller variables
@action_view.controller.instance_variables.each do |v|
instance_variable_set(v, @action_view.controller.instance_variable_get(v))
end

pdf = ::PDF::Writer.new( :paper => PAPER )
pdf.compressed = true if RAILS_ENV != 'development'
eval template, nil, "#{@action_view.base_path}/#{@action_view.first_render}.#{@action_view.pick_template_extension(@action_view.first_render)}"

pdf.render
end
end
end

And in yourapp/views/foo/bar.rpdfFile you put


pdf.select_font "Times-Roman"
pdf.text "Hello, Ruby.", :font_size => 72, :justification => :center

If you want to use ActionView helpers via this method, just use the @ action_view instance variable:


pdf.text "Price is:"
pdf.text @action_view.number_to_currency(500)

Check you're not using a layout for actions rendering an rpdf template

Note:
If you're on a Mac and you get 'jpeg marker not found 'or 'undefined
Method 'unpackage' for nil: \ NilClass (\ NoMethodError) 'errors with
Above, this seems to be a problem with Apple's version of Ruby on
Tiger. See this thread: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/145411

Installing Ruby with \ DarwinPorts is one possible solution.

If
You wowould like to have the user prompted to download the file instead
Of displaying it within the window (can be useful for handling
Validation prior to download), then add the following to your PDFRender class:


@action_view.controller.headers["Content-Disposition"] ||= 'attachment'
Ruby FPDF

An other alternative is Ruby FPDF, a port of php fpdf. it's just one small Ruby file, which can be dropped in your Rails application "lib" folder. download at http://brian.imxcc.com/fpdf/ (moved to http://zeropluszero.com/software/fpdf ?). Extends examples, plus a font generator, are supported ded.

  #in controller
def pdf
send_data gen_pdf, :filename => "something.pdf", :type => "application/pdf"
end

private
def gen_pdf
pdf=FPDF.new
pdf.AddPage
pdf.SetFont('Arial','B',16)
pdf.Cell(40,10,'Hello World!')
pdf.Output
end

Here is an example of using content stored in a database and generating a PDF with FPDF.
Here is a problem that occurs, when trying to include JPGs or PNGs into the PDF on Mac OS: ErrorUsingFPDFWithJPGOrPNGOnMacOS

Fpdf: Table allows easy adding tables to Ruby FPDF.

JasperReports

JasperReports
Is a powerful-and even more important-well known open source Java
Reporting tool that has the ability to deliver rich content in formats
Such as PDF, RTF, HTML, CSV and XML. Read HowtoIntegrateJasperReports into Rails.

NotesHeaders for Internet Explorer

Note
That you may have to play around a bit to get send_data to work
Internet Explorer. The following lines worked wonders for me (see the API docs for more info onsend_data):


if @request.env['HTTP_USER_AGENT'] =~ /msie/i
@headers['Pragma'] = ''
@headers['Cache-Control'] = ''
else
@headers['Pragma'] = 'no-cache'
@headers['Cache-Control'] = 'no-cache, must-revalidate'
end
Do not use a layout

If you are not usingsend_data, Make sure you disable layout for your pdf method.Note: This can also be accomplished by render_without_layout

class YourController < ApplicationController
layout "layouts/yourLayout" , :except => :yourPdfMethod

def yourPdfMethod
..
end
end
PDF Form Fill

Using
All the tools listed above to create a nice looking pdf file will take
You a lot of time to learn how to do. The easier way is to create
Form using Adobe Acrobat. Simply use the text field tool to create
Where dynamic text shoshould be entered in at and give them variable
Names. Now use this script to create an FDF compatible file...

def createFDF(info)
data = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; # header
data += "1 0 obj\x0d<< " # open the Root dictionary
data += "\x0d/FDF << " # open the FDF dictionary
data += "/Fields [ " # open the form Fields array

info.each { |key,value|
if value.class == Hash
value.each { |sub_key,sub_value|
data += '<< /T (' + key + '_' + sub_key + ') /V '
data += '(' + sub_value.to_s.strip + ') /ClrF 2 /ClrFf 1 >> '
}
else
data += '<< /T (' + key + ') /V (' + value.to_s.strip + ') /ClrF 2 /ClrFf 1 >> '
end
}

data += "] \x0d" # close the Fields array
data += ">> \x0d" # close the FDF dictionary
data += ">> \x0dendobj\x0d" # close the Root dictionary

# trailer note the "1 0 R" reference to "1 0 obj" above
data += "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d"
data += "%%EOF\x0d\x0a"
afile = File.new("/tmp/rails_" + rand.to_s, "w") << data
afile.close
return afile
end

This function will return your fdf temp file, Now to enter that info into a pdf you will need pdftk found at http://www.accesspdf.com/pdftk/

Once that is installed you can do something like this...

u = User.find(:first)

fdf = createFDF(u.attributes)

pdf_output = `pdftk ./user_info.pdf fill_form #{fdf.path} output - flatten`
File.delete(fdf.path)

Next just pass the exported _output to the browser for the user to get the pdf file, or save it in the database.

Happy hacking! -Chief

PDFlib and PDFlib-Lite

PDFlib newest release contains Ruby bindings. PDFlib and PDFlib-Lite is one of the fastest PDF
Generation libraries in production. This is a commercial library though
(Unless you meet their strict requirements for their opensource
License ).

For installation and usage information, you can view this 2 part series by Bob Silva

Generating PDFs in Rails-Part I-Installing
Generating PDFs in Rails-Part II-Real World Usage

Comments:
Disclaimer:
Personal Opinion. One of the things that is holding Rails away from
Enterprise is its reporting solutions (or lack of). There's no tool in
The neatness of JasperReports (yet).-Tamer Salama

Comments:
Regarding pdf form fill-Where wocould you put the script to create the FDF file? In the controller?

Rfpdf Plugin

I am a long time user of PDFlib. when I started working with Ruby on Rails, like Ruby on Rails I searched for a free PDF capable solution. I tried RTex with mixed results-sometimes it worked sometimes it didn't. then I found Ruby on FPDF. I have been very pleased.

I did like the template view capability of RTex, which accommodated embedding the ruby code in files with. rtex extensions.

I also had a client that needed Chinese, Japan and Korean support. These versions ages were supported in the PHP version of FPDF
But only Chinese had been ported and that port didn't work properly so
I spent the weekend porting these three ages to Ruby from PHP.

The Rfpdf Plugin incorporates: Ruby FPDF, e-ruby template view support (. rfpdf files) and additional Asian support for Chinese, Japanese and Korean ages.

Download it at http://rubyforge.org/projects/rfpdf/ or see
The other install/example details at Rfpdf Plugin.

From: http://wiki.rubyonrails.com/rails/pages/HowtoGeneratePDFs

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.