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

简介:
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 onto 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 for 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, rpdf files with :


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

in your config/environment.rb file, and put the following somewhere in the lib directory :


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 your app/views/foo/bar.rpdf file 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 `unpack’ for nil:\NilClass (\NoMethodError)’ errors with the 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 would 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/ ?). Many examples, plus a font generator, are included.

  #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.

Notes

Headers for Internet Explorer

Note that you may have to play around a bit to get send_data to work with Internet Explorer. The following lines worked wonders for me (see the API docs for more info on send_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 using send_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 a form using Adobe Acrobat. Simply use the text field tool to create where dynamic text should 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 pdf_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 the 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 would 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, Japanese and Korean support. These languages 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 languages 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 languages.

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

分类: Ruby on Rails
 
本文转自 RubyPdf 的中文博客博客园博客,原文链接: http://www.cnblogs.com/hardrock/archive/2006/07/24/458184.html,如需转载请自行联系原作者
相关文章
|
4月前
|
前端开发 测试技术 数据库
使用Ruby on Rails进行快速Web开发的技术探索
【8月更文挑战第12天】Ruby on Rails以其高效、灵活和易于维护的特点,成为了快速Web开发领域的佼佼者。通过遵循Rails的约定和最佳实践,开发者可以更加专注于业务逻辑的实现,快速构建出高质量的Web应用。当然,正如任何技术框架一样,Rails也有其适用场景和局限性,开发者需要根据项目需求和个人偏好做出合适的选择。
|
4月前
|
前端开发 测试技术 API
揭秘Ruby on Rails的神秘力量:如何让你的Web应用飞起来?
【8月更文挑战第31天】Ruby on Rails(简称RoR)是一个基于Ruby语言的开源Web应用框架,自2005年发布以来,因简洁的语法、强大的功能和高效的开发效率而广受好评。RoR采用MVC架构,提高代码可读性和可维护性,拥有庞大的社区和丰富的库支持。本文通过示例代码展示其强大之处,并介绍RoR的核心概念与最佳实践,帮助开发者更高效地构建Web应用。
45 0
|
4月前
|
前端开发 API C++
在Ruby世界中寻找你的Web框架灵魂伴侣:Rails vs Sinatra
【8月更文挑战第31天】在Ruby的世界里,选择Web框架如同挑选衣物,需根据场合和需求。Rails与Sinatra是两大热门框架,前者以其“约定优于配置”理念和全面的功能成为企业级应用的首选;后者则以轻量级和灵活性著称,适用于快速原型开发和小规模应用。通过对比两者特性,如Rails的MVC架构与Sinatra的简洁API,我们可以看到它们各有所长。选择合适的框架,如同找到旅途中的最佳伙伴,让开发之路更加顺畅愉悦。这场探索之旅教会我们,没有绝对的好坏,只有最适合的选择。
37 0
|
4月前
|
安全 前端开发 数据安全/隐私保护
如何在Ruby on Rails中打造坚不可摧的OAuth认证机制
【8月更文挑战第31天】在构建现代Web应用时,认证与授权至关重要。本文介绍如何在Ruby on Rails中实现OAuth认证,通过使用`omniauth`和`devise` gems简化流程。首先安装并配置相关gem,接着在`User`模型中处理OAuth回调,最后设置路由及控制器完成登录流程。借助OAuth,用户可使用第三方服务安全地进行身份验证,提升应用安全性与用户体验。随着OAuth标准的演进,这一机制将在Rails项目中得到更广泛应用。
51 0
|
5月前
|
SQL 安全 数据库
Ruby on Rails 数据库迁移操作深度解析
【7月更文挑战第19天】Rails 的数据库迁移功能是一个强大的工具,它帮助开发者以版本控制的方式管理数据库结构的变更。通过遵循最佳实践,并合理利用 Rails 提供的迁移命令和方法,我们可以更加高效、安全地管理数据库结构,确保应用的稳定性和可扩展性。
|
6月前
|
前端开发 测试技术 数据库
Ruby on Rails:快速开发Web应用的秘密
【6月更文挑战第9天】Ruby on Rails,一款基于Ruby的Web开发框架,以其高效、简洁和强大备受青睐。通过“约定优于配置”减少配置工作,内置丰富功能库加速开发,如路由、数据库访问。活跃的社区和海量资源提供支持,MVC架构与RESTful设计确保代码清晰可扩展。高效的数据库迁移和测试工具保证质量。Rails是快速构建Web应用的理想选择,未来将持续影响Web开发领域。
|
7月前
|
开发框架 安全 前端开发
使用Ruby on Rails进行快速Web开发
【5月更文挑战第27天】Ruby on Rails是一款基于Ruby的高效Web开发框架,以其快速开发、简洁优雅和强大的社区支持著称。遵循“约定优于配置”,Rails简化了开发流程,通过MVC架构保持代码清晰。安装Ruby和Rails后,可使用命令行工具创建项目、定义模型、控制器和视图,配置路由,并运行测试。借助Gem扩展功能,优化性能和确保安全性,Rails是快速构建高质量Web应用的理想选择。
|
7月前
|
监控 数据可视化 前端开发
使用Ruby on Rails构建的员工上网行为监控Web应用
我们开发了一款基于Ruby on Rails的员工上网行为监控Web应用,帮助企业保护数据安全和确保员工生产力。该应用利用Rails的MVC架构和Active Record管理数据库,通过网络代理和JavaScript追踪员工网络活动。数据收集后,应用进行分析和可视化,以便识别异常行为。此外,借助Rails的后台任务和Sidekiq gem,实现数据自动化处理和定时更新,为公司提供实时监控反馈。
323 2
|
7月前
|
缓存 前端开发 测试技术
框架分析(6)-Ruby on Rails
框架分析(6)-Ruby on Rails
|
7月前
|
缓存 关系型数据库 数据库
上云一年烧320万美元,成功“下云”六个月后Ruby on Rails之父现身回了 14 个问题!...
上云一年烧320万美元,成功“下云”六个月后Ruby on Rails之父现身回了 14 个问题!...
54 1