I am available for freelance work! Click here to email me.

Creating PDF Documents in Ruby on Rails

February 16th, 2009

Those of you that follow me on Twitter will probably know that I’ve spent the last week or so trying to find a decent way of generating PDF documents from a Rails application. I finally found a solution that suited my needs so I thought I’d share it with you lovely people.

Initial Probing

After my first round of googling I came across 2 solutions that people seem to be using:

I’ll admit I didn’t really look too much into PDF::Writer but I did spend a couple of days playing around with Prawn.

All was going well until I ran into certain situations. The two main problems I had with Prawn were that it’s too difficult to style things as I wanted to and generating a table of contents. I might be missing something but I couldn’t get my head around how to solve the latter.

I don’t want to learn a new syntax to define my PDF documents. I don’t want to learn a new way of styling things. I like my HTML and CSS.

WHERE’S MY HTML AND CSS?!

Introducing Prince

Prince XML is a command line program that takes your spiffy html and css and returns a nicely formatted PDF document for you.

Prince is a computer program that converts XML and HTML into PDF documents. Prince can read many XML formats, including XHTML and SVG. Prince formats documents according to style sheets written in CSS.

See, I told you.

So, we’ve got prince xml, but what about hooking it up with rails?

Introducing Princely

Princely is a fantastic plugin by Michael Bleigh, author of other great plugins, such as subdomain-fu and acts-as-taggable-on.

Princely is basically a wrapper around the Prince API. It allows you to define your PDF views as templates like show.pdf.erb. You can write your views as you would normal html views and Princely will return a pdf when you hit those pages.

def show
  respond_to do |format|
    format.html
    format.pdf do
      render :pdf => "filename", :stylesheets => ["application", "prince"], :layout => "pdf"
    end
  end
end

Awesome, right?

Installing Prince and Princely

To install Prince XML, visit the download page and chose the package which suits your needs. Done.

Next, to install Princely, run the following command from your Rails app.

script/plugin install git://github.com/mbleigh/princely.git

Page Numbering

To set the page numbers of you PDFs you’ll need to use the counter attribute of CSS3.

@page {
  @bottom-left {
    content: counter(page);
  }
}

This snippet of code will, funnily enough, add the current page number to the bottom left of each page. That’s all there is to it.

If you’d prefer to show the page number along with the total number of pages, you can use the following:

@page {
  @bottom-left {
    content: "Page " counter(page) " of " counter(pages);
  }
}

Table of Contents

If you’re creating a large PDF with a few sections, it’s generally a good idea to include a table of contents at the start of your document.

The first thing you need to do, is output a list of links at the start of you document that link to internal anchors:

<ul id="toc">
  <li><a href="#ruby">Chapter 1: An Introduction to Ruby</a></li>
  <li><a href="#rails">Chapter 2: Hello, Rails!</a></li>
  <li><a href="#prince">Chapter 3: Pump it up, Prince!</a></li>
</ul>

This will give you a list, that looks something along the lines of the following image:

toc

Now with a bit of CSS magic, we can add the page numbers from our PDF to the table of contents. Ready for this? Go!

ul#toc a::after
{
  content: leader('.') target-counter(attr(href), page);
}

That’s it. attr(href) finds the target of the link. target-counter finds the page that the anchor is on and the leader call just pads the left had side with periods. Win.

That nifty bit of CSS should leave you with something looking like this:

toc unstyled

A bit more CSS to tidy it up a bit:

ul#toc ul {
	margin: 0;
	list-style: none;
	padding: 0;
}

ul#toc a {
	text-decoration: none;
}

ul#toc li {
	margin: 0;
	padding: 0;
	list-style: none;
}

And then we should have something that looks a bit nicer:

toc styled

That’s all there is to it. Easy, right?

Metadata

Prince writes metadata to your PDFs via the meta tags in your html. You can set the document author, subject and keywords using the following:

<head>
  <title>PDF Generation</title>
  <meta name="author" content="Jim Neath"/>
  <meta name="subject" content="Generating PDFs with Prince"/>
  <meta name="keywords" content="PDF, prince, ruby, rails"/>
</head>

Further Reading

(Possibly) Related Posts

Recommend Me

If you found this post or anything else on this site of any use, then please take the time to recommend me on Working with Rails.

You can follow any responses to this entry through the RSS 2.0 feed. Trackback from your own site.

  • Martin
    Princely doesn't seem very friendly to Windows.
  • Falk Pauser
    Nobody seems to know about the wonderfull PDFlib. (http://www.pdflib.com/). The tryout-version contains a rails-pdflib example... Prawn and PDF::Writer are way too limited (e.g. nested tables, full controll over table-cell border-styles etc.pp) and buggy...
  • Be sure to check http://www.xhtml2pdf.com/ as well. It's written in Python, runs fast and produces really neat PDFs from HTML/CSS (at least when I did a test-run). There are lots of examples included with the source.
  • Chris
    This is very basic PDF creation. For this kind of thing id rather use jruby and a decent java library of which there are lots
  • John
    I use html2doc -- open source command line html => pdf converter...

    http://www.htmldoc.org/software.php
  • @Gregory Brown - I didn't come across Prawn::Format when I was originally looking for a solution, hence why it's not mentioned in the article. I'll definitely take a look at it.

    @Morgan Roderick - It's not that I don't like learning new things, it's just I know where I stand with html/css. The stuff we've been working on, generates PDFs that can be 100+ pages. I know I can achieve this a lot quicker in html than with Prawn's markup.

    Re: Nested statements. As far as I know you can do this in CSS3:

    @media print {
    
    body {
    font: 12px Arial, san-serif;
    }
    }


    http://www.w3.org/TR/css3-syntax/#at-media-rule
  • Nice little writeup on using PrinceXML.

    It seems a bit of a paradox, working with Rails and refusing to learn something new ;-)

    Anyhow, I just wanted to point out, that CSS does not allow for nested statements.
  • I have used both PDF::Writer and Prawn, must say that with Jamis' prawn-format, Prawn is a no-brainer.
  • It's not going to get you HTML + CSS, but I feel like you should have at least mentioned Prawn::Format:
    http://jamis.github.com/prawn-format/
  • Nilesh
    A cheaper alternative to Prince and almost as good, is PD4ML. I have written a plugin to make PD4ML work easily with Rails apps --

    http://github.com/nilesh/pd4ml-ruby/
  • Jo„o Vitor Lacerda Guimar„es
    PrinceXML looks like a nice solution to generate PDF but some details kept me away from it.

    From the download page.
    We offer other licenses to those who wish to remove the annotation, *run Prince on a server*, or embed Prince into other applications.

    Server License
    Buy now from SWREG for USD 3800
  • There is just one major caveat with your approach: a server license of prince costs about USD 3800.
blog comments powered by Disqus
Purify - Issue Tracker

Jim Neath is a 26 year old Freelance Ruby on Rails developer from Manchester, UK.

Recommend Me

Categories