37
votes

The Ruby On Rails Wiki lists a couple of libraries that facilitate PDF generation in Rails. I need to print out address labels (in letter format, thus 12-15 addresses per page) and cannot decide which one to use. Any recommendations?

10
I'm on formatting holy crusade today :)Tamas Czinege
Perhaps you were looking to generate PDFs, not "PFD"szenazn

10 Answers

9
votes

Prawn with Prawnto for sure. The DSL is a real treat, as is the simplicity of being able to treat PDF as any other format in a respond_to format block:

respond_to do |format|
format.pdf { render :layout => false }

There's a tutorial video on Prawn here:

3
votes

There's also RTeX. That works well if you're willing to translate to LaTeX first. LaTeX is a very good way to store marked-up documents. It just depends on how static each document is. If most of the document is dynamic, you might do better with Prawn or PDF::Writer. If most of it is static, with just a couple of text-replacements for each, LaTeX might be a better choice.

2
votes

If you're not doing anything too complex, You could also use HTMLDOC, which converts basic HTML to PDF. This prevents you from having to learn more proprietary layout syntax(like in the case of Prawn). It might save you some headaches :)

Here's a link to the ruby gem for HTMLDOC:

Also, here's a good guide for rendering a view in rails to pdf using HTMLDOC:

2
votes

Prawn is the way to go. Now with prawn-labels that is really easy to do.

Check out the project's README here:

https://github.com/jordanbyron/prawn-labels#readme

This is a super simple example being used in a Rails controller. Don't forget to add gem 'prawn-labels' to your Gemfile.

names = %w{Jordan Kelly Greg Bob}

labels = Prawn::Labels.render(names, :type => "Avery5160") do |pdf, name|
  pdf.text name
end

send_data labels, :filename => "names.pdf", :type => "application/pdf"
2
votes

There is also PDFKit. It's quite interesting too.

1
votes

I've used both PDF::Writer and Prawn and find Prawn much more pleasant to use. Check out Ruby Mendicant for a comparison that demonstrates the joys of Prawn w/r/t PDF::Writer.

Actually, just check out Ruby Mendicant anyway for a great design pattern for right livelihood as a developer.

0
votes

Though not completely ruby, you could use OpenOffice .odt to generate PDFs by combining serenity and docsplit.

http://github.com/kremso/serenity

http://documentcloud.github.com/docsplit/

Or you could use the clamsy gem which uses odt and cups-pdf to generate the PDF.

http://github.com/ngty/clamsy

0
votes

For your use case, I agree with most other answers that prawn is the right choice because you need pixel-level control over the output.

For generating PDF reports with built-in (opinionated) table styling, headers, etc., you can use the report gem - check out Generate pdf from Rails 3 - what tool to choose? for an example of how to use.

0
votes

I've used flying saucer for pdf generation from html. It's a java library but you can use the Ruby-Java Bridge gem to access it in your rails app. It's css 2.1 compliant and has a few additions from css3 to allow some extra control over paging. I'd recommend it as it doesn't require you to put 'pdf code' in your html, you can use the same views and partials to display to the browser as you do to generate pdfs.

Flying Saucer: https://github.com/flyingsaucerproject/flyingsaucer

Ruby Java Bridge: http://rjb.rubyforge.org/

I use this module code to generate the pdfs

require 'rubygems'
require 'rjb'

module Html2Pdf

  def self.included(controller)
      controller.send :helper_method, :create_pdf
  end

  def create_pdf(options = {})
      itext = "#{RAILS_ROOT}/lib/html2pdf/jars/iText-2.0.8.jar"
      core_renderer = "#{RAILS_ROOT}/lib/html2pdf/jars/core-renderer.jar"
      xerces = "#{RAILS_ROOT}/lib/html2pdf/jars/xml-apis-xerces-2.9.1.jar" 
      joinchar = (RUBY_PLATFORM.include? 'mswin') ? ';' : ':'
          classpath = [itext, core_renderer, xerces].join(joinchar)
      Rjb::load(classpath, jvmargs=['-Djava.awt.headless=true'])
      if options[:htmlstring].nil?
      options[:layout] ||= false
          options[:template] ||= File.join(controller_path,action_name+".pdf.erb")
          html_string = render_to_string(:template => options[:template], :layout => options[:layout])
    else
          html_string = options[:htmlstring]
    end
    # Make all paths relative, on disk paths...
    html_string.gsub!(".com:/",".com/") # strip out bad attachment_fu URLs
    html_string.gsub!( /src=["']+([^:]+?)["']/i ) { |m| "src=\"file:///#{RAILS_ROOT}/public/" + $1 + '"' } # re-route absolute paths
    html_string.gsub!( /url\(["']+([^:]+?)["']/i ) { |m| "url\(\"file:///#{RAILS_ROOT}/public/" + $1 + '"' } # re-route absolute paths
    # Remove asset ids on images with a regex // tbh i can't remember what this line is for but i'm sure it did something awesome
    html_string.gsub!( /src=["'](\S+\?\d*)["']/i ) { |m| 'src="' + $1.split('?').first + '"' } 
    filename = "#{RAILS_ROOT}/public/pdfs/"+options[:filename]+".pdf"
    fileOutputStream = Rjb::import('java.io.FileOutputStream')
      iTextRenderer = Rjb::import('org.xhtmlrenderer.pdf.ITextRenderer')
      renderer = iTextRenderer.new
      renderer.setDocumentFromString(html_string)
      os = fileOutputStream.new(filename)
      renderer.layout()
      renderer.createPDF(os)
      os.close()
  end

end

Calling it with code like this:

def generate_pdf  
  htmlsrc = render_to_string(:partial => 'invoice', :layout => false)
  rnd = Time.now.to_s(:datentime).gsub!(/[\/ \.:]/,'')
  filename = "docstore/tmp_#{rnd}"
  create_pdf(:htmlstring => htmlsrc, :filename => filename)
  contents = open("#{RAILS_ROOT}/public/pdfs/#{filename}.pdf", "rb") { |io| io.read }
  File.delete("#{RAILS_ROOT}/public/pdfs/#{filename}.pdf")
  respond_to do | wants |
    wants.html { render :text => contents, :content_type => 'application/pdf' }
  end    
end