Simple checklists with ruby and prawn

I recently finished reading Atul Gawande’s The Checklist Manifesto, which is an excellent book on why lists are good for your life. His comments on how to use checklists in everyday activities resonate with me, a chronic forgetter with a list of things to do every weekend so his bedroom doesn’t overflow with junk.

Gawande’s descriptions of checklists - simple, unadorned, and easy to read - made me want to make my own for various tasks that pop up in my life. At its heart, a checklist is a simple beast, so I started wondering if I could automate the generation of an A4 sheet of paper with suitable boxes and titles from, for example, a plaintext file.

Prawn - PDF generation for ruby

Prawn is a simple ruby PDF generator with a syntax that lets you output basic documents, while still allowing for some pretty powerful document generation.

Here’s the most basic document in Prawn (stolen right from the website:

1
2
3
4
5
require 'prawn'

Prawn::Document.generate('hello.pdf') do |pdf|
  pdf.text("Hello Prawn!")
end

The manual covers most situations you’ll come across in everyday PDF generation, including various text and text-box options, geometric shapes, and the manipulation of bounding boxes.

Bounding boxes are a pretty pivotal element in PDF generation with prawn. Each bounding box is its own set of coordinates on the page, which makes repeating elements a breeze since you can just make a new bounding box and, inside that, run your code.

This is handy for a program that basically runs through a list of items and arranges them prettily on a page. Every time I make a new item, I just have to shuttle down the page the appropriate amount (and prawn actually does that for you), make a new bounding box, and start the process again.

Here’s an example of the code I use to output a checklist item to the PDF:

1
2
3
4
5
6
7
8
9
top_margin = set_font_size(doc,14)

doc.bounding_box([doc.bounds.left,doc.cursor], width:doc.bounds.width, height:LINE_HEIGHT) do
  doc.stroke_bounds
  doc.stroke_rectangle [doc.bounds.left, doc.bounds.top], LINE_HEIGHT, LINE_HEIGHT
  doc.translate(LINE_HEIGHT+MARGIN, -top_margin) do
    doc.text my_text
  end
end

set_font_size is a helper method that will set the font size appropriately, and handily returns the top margin such that everything will line up nicely. Following this I set up a bounding box to contain the checklist item, draw a line around it, then make a little square checkbox, as wide as the line is high. Finally, I translate across the page slightly and write some text. The magic here is in the property doc.cursor, which will change as I write text.

I have little blocks of code like this for headings and items marked done - but that’s all I have (and, I think, all I need) for this little project.


There’s a few other cool things in this project (I like the line-matching/priority mechanism quite a bit), but I’ll leave you to explore them as you see fit. This is a pretty simple project in prawn, but it gives a bit of an idea of its power.