Key Takeaways
- Prawn is a Ruby-based platform for generating portable documents, offering a hackable platform that can be easily tweaked. It is the successor to PDF::Writer and is well-maintained, allowing tools to be built on it.
- Prawn provides commands for text styling, cursor movement, and image addition. It allows for the use of local paths or external URLs for images, and supports backgrounds. However, it may require some tinkering with the size of the background image.
- While Prawn’s Domain Specific Language (DSL) approach may not be for everyone, it is fairly popular. Alternatives include HTML+CSS to PDF, which is seen as the future of programmatic typesetting. Tools like wkhtmltopdf wrapped by the PDFKit gem offer this capability.
Generating portable documents can be tricky. For the most part, this task has moved from typesetting languages to WYSIWYG editors like Scribus and Adobe InDesign.
But automation has its own benefits. Instead of the kitchen-sink, it’s nice to have a hackable platform that can be easily tweaked to get us what we want.
Prawn is the spiritual successor to PDF::Writer
. It is currently well-maintained and, like many other Ruby projects, Prawn is intended as a platform on which tools can be built.
LaTeX
Before we get into Prawn, the elephant deserves mentioning.
TeX was written by Donald Knuth and released in 1978. At that time, digital printing was fairly new and Knuth had just gotten back a galley proof of his second volume of “The Art of Computer Programming.” He was shocked by the quality and decided he could do a better job in a few months. It ultimately took him a decade (source).
TeX is famous for being bug free. Knuth had schemes in which those who found bugs in his documents or programs were paid a fee. The TeX program’s rewards followed the Wheat and Chessboard Problem, starting at $1.28 and reaching $327.68. Typical of Knuth, the version number of TeX is converging to π.
LaTeX (pronounced “lahtehk”) is a macro package for TeX that makes it easier to produce standard documents. These days, TeX is often referred to as LaTeX, and it is the de facto standard for creating scientific or mathematical documents in academia.
Here’s an example of LaTeX:
\documentclass{article}
\title{LaTeX Hello World}
\author{Robert Qualls}
\today
\begin{document}
\maketitle
Hello World
\end{document}
Those interested in LaTeX packages would want to check out CTAN, the Comprehensive TeX Archive Network, and the LaTeX rubygems. Bonus points if you recognize the similarity to CPAN: the Comprehensive Perl Archive Network.
Installing Prawn
If you’re running a Rails operation, LaTeX isn’t the most convenient approach for generating documents. There are some gems available, but it’s really nice when the logic and the presentation are in the same language. That’s what Prawn supplies.
First, we need to install the Prawn gem:
gem install prawn
We can verify that Prawn is working with the following test:
require "prawn"
Prawn::Document.generate("hello.pdf") do
text "Hello World!"
end
Making Text
Most of Prawn’s commands are fairly minimalistic and what you would expect:
require "prawn"
Prawn::Document.generate("styling_text.pdf") do
text "Default text styling"
text "Blue 16pt Helvetica", size: 16, font: "Helvetica", color: "0000FF"
text "Aligned Center", align: :center
font_size 12
font "Courier" do
text "Size 12 Courier"
font_size 10 do
text "Slightly smaller Courier"
end
end
text "Default font size 12"
font "Helvetica"
3.times do |i|
text "Helvetica with leading 10 line #{i}", leading: 10
end
end
Consistent with Ruby, Prawn provides multiple ways to accomplish the same thing. Most DSL methods can optionally be scoped with a block.
Moving Around
There are two important geometric locations in Prawn:
- the origin
- the cursor
The origin, [0,0] is at the bottom-left corner of the document. This can be confusing as the cursor starts in the top-left corner.
“[0,0] what?”, you might ask. The default unit in Prawn is a PDF Point, where one PDF Point is equal to 1/72 of an inch. Because America.
Moving the cursor around is as simple as move_down
, move_up
, or move_cursor_to
:
require "prawn"
Prawn::Document.generate("moving_around.pdf") do
text "#A At the top, cursor position #{cursor}"
move_down 50
text "#B Down 50, cursor position #{cursor}"
move_cursor_to bounds.bottom + font_size
text "#C at the bottom, cursor position #{cursor}"
move_up 50
text "#D Up 50 from #C, cursor position #{cursor}"
move_cursor_to bounds.top / 2
text "#E In the middle, cursor position #{cursor}"
end
Adding Images
Prawn lets us use either local paths or external URLs with open-uri
:
require 'prawn'
require 'open-uri'
Prawn::Document.generate("image.pdf") do
text "Dog", align: :center, color: "333333", size: 42
move_down 20
text "Homo sapiens' best friend", align: :center, color: "555555", size: 26
url = "https://pixabay.com/static/uploads/photo/2014/03/14/20/13/dog-287420_960_720.jpg"
image open(url), fit: [500, 500], position: :center
end
It also supports backgrounds. Unfortunately, Prawn doesn’t seem to offer any way to fit the background when done this way, so you may need to tinker with the size. This can be done with ImageMagick and the mini_magick
gem:
$ brew install imagemagick
$ gem install mini_magick
I decided to go with 650×950 for the image I used:
require 'prawn'
require "mini_magick"
url = "https://pixabay.com/static/uploads/photo/2015/11/19/08/12/milky-way-1050526_960_720.jpg"
filename = "fitted_background.jpg"
image = MiniMagick::Image.open(url)
image.resize "650x950"
image.write filename
background = filename
Prawn::Document.generate("background.pdf", background: background) do
options = { align: :center, valign: :center, leading: 25, color: "C1C1C1" }
text "\"Somewhere, something incredible is waiting to be known\"", options.merge({ size: 20 })
text "- Carl Sagan", options.merge({ size: 18 })
end
Book Cover
Here is the source for the fake book cover.
require "prawn"
Prawn::Document.generate("oruby_cover.pdf") do
move_down 60
image "shrimp.png", fit: [500, 400], position: :center
move_cursor_to bounds.top
shape_color = "008888"
font "Times-Roman"
fill_color shape_color
fill_rectangle [0, bounds.top], bounds.width, 20
move_down 25
fill_color "000000"
text "Because InDesign is for scrubs", :size => 20, :style => :italic, :align => :center
bounding_box([0, bounds.top - 50], :width => bounds.width, :height => bounds.height) do
move_down 380
text "using", :size => 40, :style => :italic
move_up 380
fill_color shape_color
fill_rectangle [0, 300], 550, 200
fill_color "FFFFFF"
move_down 410
font "Times-Roman"
text "Prawn", :size => 165, :align => :center
fill_color "000000"
font "Helvetica"
draw_text "O'RUBY", :at => [0, bounds.bottom + 50], :size => 30
move_to [0, bounds.bottom + 100]
font "Times-Roman"
draw_text "Robert Qualls", :at => [bounds.right - 100, bounds.bottom + 50], :size => 20, :style => :italic
end
end
Image courtesy of Pearson Scott Foresman(Source)
Conclusion
If you want to learn more about Prawn, the Prawn team has a nice manual.
Prawn’s DSL approach isn’t for everybody nor the best solution for all situations. In fact, HTML+CSS to PDF is probably the future of programmatic typesetting. Hardcoding instead of flexible design feels very 20th century, but Prawn is fairly popular, so there’s a good chance you will encounter it from time to time. Thankfully, we have wkhtmltopdf wrapped by the PDFKit gem. If you want to see a comparison, be sure to read PDF Generation in Rails.
Frequently Asked Questions (FAQs) about Prawn PDF Typesetting in Ruby
What is Prawn in Ruby and why is it used?
Prawn is a pure Ruby PDF generation library that enables developers to create complex PDF documents. It’s used because it provides a lot of flexibility and control over how the PDFs are generated. With Prawn, you can add images, graphics, formatted text, and more to your PDFs. It’s also very efficient and can generate large PDFs quickly.
How do I install Prawn in Ruby?
To install Prawn, you need to add the gem to your Gemfile. You can do this by adding the line gem 'prawn'
to your Gemfile and then running bundle install
. This will install the Prawn gem and make it available for use in your Ruby application.
How do I generate a PDF with Prawn?
Generating a PDF with Prawn involves creating a new Prawn::Document and then using methods like text, image, and stroke to add content to the PDF. Once you’ve added all your content, you can save the PDF to a file with the render_file method.
Can I add images to my PDFs with Prawn?
Yes, Prawn supports adding images to PDFs. You can use the image method to add an image, specifying the path to the image file and the position where you want the image to appear in the PDF.
How do I add formatted text to a PDF with Prawn?
Prawn provides several methods for adding formatted text to a PDF. You can use the text method to add plain text, and the formatted_text method to add text with various styles and formats. You can also use the font method to change the font used for the text.
Can I draw shapes and lines in my PDFs with Prawn?
Yes, Prawn includes methods for drawing a variety of shapes and lines in your PDFs. You can use methods like stroke_horizontal_line, stroke_rectangle, and fill_circle to draw lines, rectangles, and circles, respectively.
How do I save my generated PDF to a file with Prawn?
Once you’ve added all your content to your Prawn::Document, you can save it to a file with the render_file method. You just need to pass the path where you want the file to be saved as an argument to this method.
Can I generate multi-page PDFs with Prawn?
Yes, Prawn supports generating multi-page PDFs. You can use the start_new_page method to start a new page in your PDF.
How do I add a table to my PDF with Prawn?
Prawn includes a Table class that you can use to add tables to your PDFs. You can create a new Prawn::Table, add rows to it with the rows method, and then draw it in your PDF with the draw method.
Are there any known vulnerabilities with Prawn?
As with any software, there may be potential vulnerabilities with Prawn. It’s always a good idea to keep your software up to date and to follow best practices for secure coding. You can check for known vulnerabilities on websites like Snyk.
Robert is a voracious reader, Ruby aficionado, and other big words. He is currently looking for interesting projects to work on and can be found at his website.