Railsmagazine60x60 Prince: Powerful PDF Generation (for a King’s Ransom?)

by Michael Bleigh

Issue: Winter Jam

published in December 2009

Michael bleigh photo

Michael Bleigh is the Creative Director and Open Source Activist at Intridea, Inc., a leading agile software consultancy focused on Ruby and Rails.

He has authored numerous open source libraries for Ruby including Princely, SubdomainFu, TwitterAuth, and TweetStream.

As expressed by Rodrigo Rosas in Rails Magazine issue 4, there are many ways to handle PDF generation in the Ruby and Rails world. These solutions come with varying degrees of effort, learning, and success. There is, however, one solution that provides powerful and flexible generation of PDFs simply by writing HTML templates. For a web application, this can be an ideal solution.

Enter Prince XML

Prince XML [1] is a PDF generation library that utilizes HTML and CSS as source material, outputting a well-styled, professional-looking PDF document in the process. The results are impressive, and Prince even provides custom CSS rules to handle print-specific concerns such as page-breaking, image pixel density, and more.

I came across Prince when I needed slick, polished PDFs for a project that was on a severe time crunch. The prospect of (mostly) re-using the existing HTML templates for the application sold me on the benefits, but there is one major drawback: price.

Prince XML is a commercial software library with a range of licenses, ranging from a personal license (one user, one computer) at $495 to a network license at $3,800. You can download a free version of Prince that will simply watermark the generated PDFs (great for local development) and you can also contact the vendor to negotiate OEM licenses for software and applications.

The cost of Prince may make it beyond the range of some projects, but if you are working on a web application and PDF generation is near the heart of it, I would strongly recommend considering what I believe is the most powerful and flexible PDF library available to web application developers.

To use Prince you just need to install it. They have downloads available for most operating systems, so simply follow the instructions on their website to get it running.

Princely: Prince on Rails

Princely [2] is a Rails plugin that provides you fast and easy access to Prince’s PDF generation capabilities within an application. To get started, all you need to do is add the Princely gem to your Rails environment.

config.gem ‘princely’, :source => ‘http://gemcutter.org’

Alternatively, you can install Princely as a plugin in your Rails application:

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

Now you’re ready to start generating PDFs. The best thing about Princely is that it provides hooks to the standard Rails render stack by adding a PDF mimetype to your application. Once the plugin is installed, you can render a PDF from a Rails template simply by calling render :pdf.

Let’s examine some of the features of Princely and Prince XML can provide through a hypothetical real-world example. Let’s say you’re building an online application for scholarly articles and you want to be able to easily generate PDF versions of the articles. We’ll walk through the architecture of the application and it’s basic HTML layout and then show how to turn that HTML into a PDF with almost no modification to the markup. The method we can use to add PDF using Prince is somewhat similar to adding Javascript to an application with progressive enhancement: we can add it in to our existing application unobtrusively and utilizing what’s already there.

The Application Setup

We’ll begin with a simple controller setup to display articles stored in Textile format. In a brand-new Rails application, install the Princely plugin using one of the methods outlined above and then generate an ArticlesController:

script/generate controller articles

Now let’s build the basic controller and action. We won’t be bothering with any of the ActiveRecord setup, instead we’ll write a quick class that will generate fake articles for us using the Faker [3] gem (add config.gem ‘faker’ to your environment.rb to use it). Put this into models/article.rb.

class Article

  def self.find(*args)

    Article.new

  end

 

  def title

    @title ||= Faker::Lorem.words(6).join(' ').titleize

  end

 

  def body

    arr = Faker::Lorem.paragraphs(13)

    [2, 6, 10].each do |i|

      arr.insert(i, "h3. #{Faker::Lorem.sentence}")

    end

    @body ||= arr.join("\n\n")

  end

 

  def body_html

    RedCloth.new(self.body).to_html

  end

 

  def author

    @author ||= Faker::Name.name

  end

end

This will stub out the basic information we need for our “scholarly article,” so we can move on to building the controller. It’s a simple action that we will later add to when adding PDF support.

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
   end
end

Let’s create a basic HTML view for our article in articles/show.html.erb.

<div class='header'>

  <h1><%= @article.title %></h1>

  <span class='author'>by <%= @article.author %></span>

</div>

 

<div class='body'>

  <%= @article.body_html %>

</div>

Finally, we need to add in the routing for to make it accessible from our application. Add this to config/routes.rb:

map.resources :articles

If you boot up your application, you should be able to see a fake article in HTML by going to /articles/1234.html (any number will do since we don’t actually care about the ID in our faked-out example).

Princely in Action

Once we’ve gotten our basic HTML application set up we can add Prince PDF support quite easily. We will be doing so in two steps: first we will update our controller to output PDF content and second we will create a custom print stylesheet to make the output look nice. To update the controller, we simply need to add PDF to a new respond_to block:

class ArticlesController < ApplicationController

  def show

    @article = Article.find(params[:id])

 

    respond_to do |format|

      format.html

      format.pdf {

        render :pdf => @article.title,

          :template => "articles/show.html.erb",

          :stylesheets => "prince" }

        end

    end

  end

What we are essentially doing here is saying “If the article is viewed with the PDF format, render a PDF named after the title using the same template as the HTML and with a special stylesheet called ‘prince’.” In fact, if you now visit /articles/1234.pdf in your application, you should be prompted to download a PDF file with a random Lorem-ipsum title. You’re already most of the way there! The output should look something like this:

While the output already works, the reason we’re using Prince in the first place is to effortlessly add a professional-looking PDF version of our scholarly article. The real power of Prince comes in using the power of CSS to style a PDF document. Create a new stylesheet at public/stylesheets/prince.css with this inside:

body {

  font-family: "Book Antiqua", serif;

  font-size: 11pt;

}

 

div.body {

  column-count: 2;

}

 

div.header {

  background: #036;

  color: white;

  padding: 0.25in;

  border-radius: 0.25in;

  margin-bottom: 0.3in;

}

 

h1 {

  string-set: doctitle content();

  margin: 0 0 0.1in;

}

 

span.author {

  font-size: 1.5em;

  string-set: docauthor content();

  margin: 0;

  color: #bcf;

}

 

h3 {

  font-style: italic;

  colro: #036;

}

 

@page {

  font-family: "Book Antiqua", serif;

  margin: 0.75in;

 

@top-left {

  content: string(doctitle);

  font-weight: bold;

}

@top-right {

  content: string(docauthor);

  font-style: italic;

}

@bottom {

  content: counter(page);

}

}

Most of this looks like (and is) ordinary CSS styling. Prince will pick it all up and apply it appropriately, letting you use the tools with which you’re already familiar. There are, however, some Prince-specific CSS properties [4] that you can use to work best with the print medium. In this example, we have used column-count to split the article into two columns. We have also used the @page selector to supply header and footer information as well as the page margins. You can set content to be used in these special CSS selectors with the string-set CSS property. You can also easily add a page counter as you can see in the @bottom section.

Once you’ve added the PDF generating CSS you can visit /articles/1234.pdf again and your output will look something like this:

That’s it! With just a plugin installation, a quick change to a controller, and a short stylesheet we’ve created a professional-looking PDF version of our article using the exact same markup as the HTML version of the page. Of course you can go well beyond the basic example here (for instance, hiding certain elements in the print version with display: none). You can also apply custom layouts to your HTML content using the render :pdf helper.

Go Forth and Print

This example was extremely simple, but that’s the beauty of Prince and Princely: you can create top-quality PDFs with as little effort as possible. I’ve found the quality and usefulness of Prince to far excel what I’ve been able to achieve with any other PDF library.

The cost of Prince makes it a bit daunting to include in your application, but if print functionality hits at the heart of what you’re trying to accomplish, you’ll be hard-pressed to find a better solution. And, with Princely, PDF generation in Rails has never been easier.