Skip to content

Generating PDFs from Ruby on Rails

by andy on March 3rd, 2010

The problem

Ruby on Rails provides various helpers for generating dynamic web content but sometimes you need documents that users can easily store and share between them. The ubiquitous file format for this is Adobe PDF nowadays.

The common solution fro this problem is Prawn [github, introduction]. Alas it requires a custom DSL for document description, no existing Rails views or partials can be reused. An alternative is princeXML which transforms HTML/CSS into pdf through an external binary. This would allow reuse of existing templates and knowledge (think CSS designers) but has the downside of its price tag of $3800.

The solution

Enters wicked_pdf: it utilizes wkhtmltopdf to create a PDF document from a Rails HTML template. HTML rendering is done through the well-known webkit-engine. This allows developers to do PDFs in the right way™: define the document’s structure through a simple HTML document and theme them through CSS. You’ll get the automatic benefit of themability: exchange the CSS and you have another format. There are also lots of CSS artists out there that can supply you with different designs.

I’ve used this solution with a test Ruby on Rails 3.0-beta application, the steps involved were:

Install wkhtmltopdf:

This program will convert the HTML into the PDF format. To make it work you’ll need at least version 0.9 which isn’t installed in Ubuntu 9.10 by default. Just download the static compiled version from the wkhtmltopdf website and place it under /usr/local/bin.

Install the wicked_pdf plugin:

Just install it in the usual Rails way:

git submodule add git://github.com/mileszs/wicked_pdf.git vendor/plugins/wicked_pdf

Additionally a sample configuration file is needed. You could use the plugin’s generator script for it but alas this didn’t work for me with Rails 3.0. Let’s just copy it form the plugin’s directory into config/initializers.

$ cp vendor/plugins/wicked_pdf/generators/wicked_pdf/templates/wicked_pdf.rb config/initializers

and alter the wkhtmltopdf path within it:

  WICKED_PDF = {
       :exe_path => '/usr/local/bin/wkhtmltopdf-amd64'
  }

Add pdf instructions to your controller..

For example I’m using the invoice#index action to render a simple PDF document.

  format.pdf do
          @example_text = "some text"
          render :pdf => "file_name",
                 :template => 'offers/show.pdf.erb',
                 :layout => 'pdf',
                 :footer => {
                    :center => "Center",
                    :left => "Left",
                    :right => "Right"
                 }
  end

We are rendering the view with a predefined footer containing some sample “center”, “left” and “right” strings. The render :pdf call has various options which can be seen on the wicked_pdf homepage.

.. and create PDF templates

The other half of the PDF templates is the view code consisting of a special layout and template for pdf generation.

The layout (app/views/layouts/pdf.html.erb) resembles a normal Ruby on Rails layout file:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <%= wicked_pdf_stylesheet_link_tag "pdf" %>
  </head>
  <body>
    <div id="content">
      <%= yield %>
    </div>
  </body>
</html>

The view code should not be too surprising, just place it under app/views/offers/index.pdf.erb :

<div id="someid"><%= @example_text %></div>

Lets also create a simple CSS file (public/stylesheets/pdf.css):

#someid {
  margin-left: 430px;
  display: float;
  height: 150px;
  background-color: green;
  width: 250px;
}

This is the whole rendering code.

Reference the PDF view and download it

We still need a reference to the newly created rendernig link, we can easily create this through:

link_to 'Create PDF document', invoice_path(@invoice, :format => :pdf)

When the link is clicked a pdf document will be generated and downloaded.

Conclusion

wicked_pdf allows easy pdf generation in a very Ruby on Rails’ way. It’s free, it works and is easy to employ..

..but not everything is perfect within the wicked_pdf world, especially error handlnig is lacking sometimes. When you’ve debugging initial problems you can expect the only error feedback to be a HTTP return code of 406. Just start with limited controller (rendering) options and double check that you’ve referenced the right view and layout paths and you should be fine.

No related posts.

16 Comments
  1. Your post has been linked at http://www.DrinkRails.com blog!

  2. Any idea if this will work on Heroku? Can you somehow install the program as an executable into the app’s folder structure instead of in /usr/local/bin?

    • the binary is just one statically compiled program: so as long as you get the architecture right (which should be linux-amd64 in 90% of the cases) you can just drop the binary in $RAILS_ROOT/utils/wktohtml-amd64 and configure it throught the $RAILS_ROOT/conf/initializers/wicked_pdf.rb file:

      WICKED_PDF = {
             :exe_path => 'utils/wkhtmltopdf-amd64'
        }
      
    • working heroku sample app: http://github.com/jordan-brough/heroku-pdf/#readme

      tip: heroku binaries need to live inside rails-root/bin.

  3. david permalink

    do you have a way to get this to work on windows?

  4. Rob permalink

    I’ve got it working under windows – almost. wkhtmltopdf works just fine. I’ve set up the :exe_path to point to proper location, but get a permission denied error. Any other windows users out there have a similar problem?

  5. Marina permalink

    I’m new to Ruby on Rails and trying to get wicked_pdf working on linux. I have the link working on the “users” page which invokes Adobe and saves the pdf file. However, Adobe can’t open the resultant pdf – says, it’s invalid format or corrupt.

    I followed all the instructions on this page. I suspect it has something to do with the template:

    :template => ‘offers/show.pdf.erb’

    What is the template? The pdf form that is used to convert “Invoices” html page (in your example) to a pdf? If so, do you have an example of this template “show.pdf.erb”?

    Any info is greatly appreciated!
    Thanks.

    • Crap, I must have confused index.pdf.erb with show.pdf.erb

      You can just create a show.pdf.erb and place valid ERB (html) code within it. So you can try it with just a “testtext“. WicketPDF will use this (in conjunction with the pdf.html.erb template) for creating a html page which in turn is then converted to a pdf document and delivered to the client’s browser.

      • Marina permalink

        Thanks, Andy. I figured it was a mistake and tried creating the index.pdf.erb (in my application, i need to convert my users index.html.erb into a pdf, so using index as a template) and it worked ! I also got rid of the “corrupt pdf” error – found solution under “issues” section from the main wicked_pdf page.

        So, now I have a pdf that shows me a list of users in a basic html format. However, it’s not picking up my css styling. I do have a pdf.html.erb layout file that looks like your example above, except I changed the value ‘pdf’ to ‘scaffold’ to point to our scaffold.css, still no difference.

        Am I missing something?

        Thanks again for the info!

        • Marina,

          I think if you specify the css using an absolute (instead of relative) path that will fix your issue with it not picking up the css.

          • I’m not too sure about it, I’ve been using relative paths without problems. @marina: how did you name your css file? Could you please post the css include statement from the template? cheers,

  6. Marina permalink

    this is from pdf.html.erb layouts:

    where I have “scaffold.css” :

    # ls -l public/stylesheets/
    total 48
    -rw-rw-rw- 1 root root 19817 Aug 9 10:52 application.css
    -r–r–r– 1 root root 19702 Aug 9 10:50 application.css.bck
    -rw-r–r– 1 root root 114 Aug 9 12:43 pdf.css
    -r–r–r– 1 root root 889 Aug 6 16:13 scaffold.css

    pdf.css was as test. I need to use either “application.css” or “scaffold.css”.

    Thanks!

  7. Marina permalink

    sorry… html code disappeared once pasted here. I’ll try again:

    here’s the line from pdf.html.erb (minus back slashes)
    “\”

  8. Marina permalink

    ok… one more time:
    “wicked_pdf_stylesheet_link_tag “scaffold” “

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS

Login with Facebook: