A Simple Way to Generate formatted PDF Documents
The Problem
Most of the applications we make are in aid of current manual systems. The whole reason clients come to us is because they want to streamline and automate parts of their processes but they still have to deal with the reality of the current, paper-based way of doing things. So it happens that recently I’ve gotten a sudden influx of requests to be able to generate printable pre-filled forms.
There are multiple ways to go about doing this. The easiest way would’ve been to just allow the user to click ‘print’ on their browser and serve them a formatted print.css page. However, a couple of projects back we ran into a problem using this approach – we could never get the same result across multiple computers. We tried restricting the browsers used to firefox but even that didn’t play nice with the differing operating systems. Using CSS to generate printable forms wasn’t an option.
The next ideal way would be to be able to generate a pdf using from an html output. This would be great because we could maybe use the existing templates and just generate the pdf from that. After looking around a while it seemed that PrinceXML was the way to go. Unfortunately for me, it didn’t come free. Alternatives such as HTML Doc just didn’t cut it.
Next in my quest was pdftk I thought maybe I could just design the templates by hand using Scribus and then just fill_form the values using pdftk. Again I hit a roadblock when I found that some of the forms had dynamic content (e.g. tables) that couldn’t be handled by this approach.
I was back to using PDF::Writer. The problem with pdf-writer is that it’s pretty raw. It allows you to do a lot of things with PDFs but you have to deal with lines, points, x/y coordinates, and all of the details. I didn’t have the time nor the patience to deal with all that for the amount of printables that were coming in.
The Solution?
So I decided to write my own little library that would fix all that. Introducing PDF::Cell – it’s a library based on PDF::Writer that allows you to layout documents in a straightforward manner without thinking about the nitty-gritty details about how exactly to position the elements. The model for laying out stuff is loosely based on Ruby Shoes’s concept of stacks and flows. Here’s an example:
pdf = PDF::Writer.new
biodata = PDF::Cell::Base.new(pdf)
biodata.build :width => 500, :font_size => 8 do
header "Crew File #245: Jack Black (2/M of MV Nautilus)", :shaded => true,
:font_size => 15
desc "The Content"
cell :width => 375 do
cell :width => 125 do
text "<b>Given Name</b>"
text "Jack"
end
cell :width => 125 do
text "<b>Middle Name</b>"
text "James"
end
cell :width => 125 do
text "<b>Family Name</b>"
text "Black"
end
cell :width => 250 do
text "<b>Address</b>"
text "#124 Main Street"
end
cell :width => 125 do
text "<b>Contact No</b>"
text "123-456-789"
end
end
cell :width => 125, :height => 125 do
text "Photo Here"
end
end
And here’s the result of the above code
Now for the explanation. The overall box is given a width of 500. (it uses pdf-writer’s measurements not mine) The width is strict – meaning everything else will adjust, just not the width. We add a cell to the box with 375 width – let’s call this the “Content” box. Within the Content box we add 3 cells each with a width of 125. These 3 boxes add up to exactly 375 – the limit of the Content box. Then we add another cell with a width of 250. Clearly this won’t fit into the Content box’s width so PDF::Cell automatically puts it below the previous 3 cells! And that’s basically it. Using that simple concept of cells you can potentially generate any layout your document needs.
I recently added support for tables within cells. Images within cells are coming up shortly although, if you know how to use pdf-writer, you can hack it yourself for now.
Installation
To install just copy/paste the following into your rails root directory:
ruby script/plugin install svn://svn.upstrat.com/plugins/pdf_cell/trunk
That’s it! You should be ready to go.