This article was originally written by Aestimo Kirina on the Honeybadger Developer Blog.
We live in a world of documents. In almost every aspect of business and life, there's a document involved. Whether you are working with invoices and contracts at your workplace or enjoying your favorite mystery thriller, in one way or another, a document is involved. The file format that forms the basis of most of these documents is the versatile portable document file format, more commonly referred to as PDF.
In the context of a Ruby or Rails app, a PDF file can be produced by converting existing HTML/CSS files into PDF documents or by using a domain-specific language (DSL) specifically built for PDF generation. In this article, we will explain how to use these two methods to generate PDFs using three different gems: PDFkit and WickedPDF, which use the HTML/CSS-to-PDF method, and the Prawn gem, which uses a powerful DSL instead.
We will also discuss how to style your PDF files to improve their user-friendliness, including how to embed images into them. Then, to wrap up, we'll outline how you can use Action mailer to send emails with PDF attachments.
Let's get into it.
Prerequisites
To follow along with the examples in this article, you'll need a few basics covered:
- Ruby 2.0+ installed
- Bundler installed
- Rails 6.0+ installed
- A basic to intermediate command of Ruby (and Rails)
Brief Intro to the PDF File Format
PDF stands for "portable document format", a file format developed by Adobe in the early 90s to make it a breeze to present documents without relying too much on the underlying system software, hardware, or operating system.
So, let's see how we can use this popular file format in the context of a Rails app. To do that, we'll check out three popular gems for generating PDFs.
WickedPDF
We’ll start with the WickedPDF gem, which is powered by the wkhtmltopdf command-line library.
This gem works by converting HTML/CSS documents into their PDF equivalents.
When to Use WickedPDF
If you'd rather use the HTML/CSS skills you already have instead of learning a new DSL to generate PDFs, then the WickedPDF gem is a good choice.
Installing and Using WickedPDF
Add the gem (include the wkhtmltopdf gem since it powers WickedPDF) to your app's Gemfile, and then run
bundle install
:
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
After that's done, go ahead and run the generator to get the WickedPDF initializer file:
rails generate wicked_pdf
Then, we'll need to tell our Rails app about the PDF file extension by editing the mime_types.rb initializer file found in the
directory.
_Tip: In very simple terms, a mime type is an identifier of file formats and how such formats should be transmitted across the Web._
```ruby
Mime::Type.register "application/pdf", :pdf
With that, our Rails app knows how to respond to the PDF file type.
Finally, you'll need to edit your controller file to respond to the PDF file format (in my case, I'll work with the show action of an orders controller, but you can use this in whatever controller your project is using where you need PDF file generation):
class OrdersController < ApplicationController
def show
respond_to do |format|
format.html
format.pdf do
# It's important to include a corresponding view template; otherwise, you'll get a missing template error.
render pdf: "#{@order.id}", template: 'orders/show.html.erb'
end
end
end
Now, if you append ".pdf" to your show view,
, you'll be greeted by the corresponding PDF view. Yay!
So far, our app can generate order PDFs. However, what happens when a colleague in logistics requests these order documents to be sent over as email attachments? How would we go about doing that?
## PDF Email Attachments Via WickedPDF And ActionMailer
Extending our order app example, let's assume a colleague in logistics needs the details of each order that comes in as an email attachment.
First, we create a relevant mailer:
```bash
rails generate mailer OrderMailer new_order
Edit the generated mailer to include the PDF file attachment:
class OrderMailer < ApplicationMailer
def new_order
@order = Order.first
# The attachment...
attachments["Order-#{@order.id}.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(template: 'orders/show.html.erb', pdf: "Order-#{@order.id}")
)
mail to: "to@example.org"
end
end
With that, visit
to view the email preview (note the PDF attachment included in the email):
![Email preview with attachment](https://www.honeybadger.io/images/blog/posts/ruby-pdfs/email-preview.png)
## Prawn PDF
[Prawn PDF](https://github.com/prawnpdf/prawn) is a pure Ruby PDF- generation library that comes packed with features, such as PNG and JPG image embeds, generated file encryption, right-to-left text rendering, a way to incorporate outlines for easy document navigation, and a lot more.
Prawn comes with its own DSL, which drives its powerful PDF generation abilities.
### When to Use Prawn
Although it's not a fully featured report generation library like the well-known [Jasper Reports](https://community.jaspersoft.com/), with a bit of work using it's powerful DSL, you can generate some really cool and rather complex PDF documents with Prawn.
Even so, it's important to note that Prawn isn't everything. If you want to generate PDFs from HTML, then you should look elsewhere, as the gem provides very limited support for inline styling, something of a hurdle if you're working with rich HTML documents.
### Installing and Using Prawn
To get started with Prawn, install it with this command:
```bash
gem install prawn
Or, if you have an existing Rails app, edit the Gemfile with the following:
gem 'prawn'
Then, run bundle to have it installed for your app.
With the gem successfully installed, just like we did with the WickedPDF gem, you will need to make your application aware of the "PDF" file type by registering it as a mime type.
Open the
file and add the following line:
```ruby
Mime::Type.register "application/pdf", :pdf
Now, to see the gem in action, go to the controller you want to respond with a PDF (I’m using the same example as I did for the WickedPDF code example):
class OrdersController < ApplicationController
def index
@orders = Order.all
end
def show
@order = Order.find(params[:id])
respond_to do |format|
format.html
# Here, we define our new PDF file format
format.pdf do
# ...instatiate a new Prawn document
pdf = Prawn::Document.new
# ...then specify the data to be included in the PDF document
pdf.text "This is order no: #{@order.id}"
# ..and finally render the PDF file
send_data pdf.render,
filename: "#{@order.id}.pdf",
type: 'application/pdf',
disposition: 'inline'
end
end
end
def new
@order = Order.new
end
end
The Prawn gem's DSL can also be used to draw complex vector images, as well as embed images into the generated PDF documents (for the purposes of this article, we'll only show the image embed functionality).
Adding Images to PDFs Using Prawn
Hopefully, you now have a basic idea of how to style PDF documents in your Rails app. However, what if you want to add images?
Using our very simplified Prawn gem example from earlier, this is how you would do it (only the show action is shown):
def show
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
pdf.text "This is order no: #{@order.id}"
# Adding an image to a PDF file...
pdf.image Rails.root.join("public", "images", '1.png').to_s, width: 450
send_data pdf.render,
filename: "#{@order.id}.pdf",
type: 'application/pdf',
disposition: 'inline'
end
end
end
Obviously, the gem packs more punch than what we've shown so far. To delve deeper, head over to the Prawn website.
PDFkit Gem
Like the WickedPDF gem, the PDFkit gem uses wkhtmltopdf on the backend to convert plain HTML and CSS files into PDF documents.
When to Use PDFkit
If you need to do a lot of HTML to PDF conversion, then this gem would make a good choice.
Using PDFkit
Add the gem:
gem install pdfkit
Remember to install the wkhtmltopdf library. Just follow the instructions found here to do so.
Once that is done, go ahead and create a simple Ruby file in your favorite folder (mine's called "testing-pdfkit.rb", but you can call yours whatever you want.)
Then, edit your file to look like the one below:
require "pdfkit"
kit = PDFKit.new(<<-HTML)
<h1>My PDFkit Invoice</h1>
<table>
<thead>
<tr>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item #1</td>
<td>$27.00</td>
</tr>
<tr>
<td>Item #2</td>
<td>$100.00</td>
</tr>
</tbody>
</table>
HTML
kit.to_file("pdfkit_invoice.pdf")
Run
ruby testing-pdfkit.rb
to generate a PDF file in the root folder of your script.
The example we've shown will produce a very plain PDF document. However, with some basic styles applied, we can make it look much better.
Applying Basic Styles to PDFs Using PDFkit
There's a good chance the PDF documents you interact with in your daily life, whatever they may be, such as a favorite ebook or a company report, will look polished and professional.
Next, let's add some styling to our code example using the PDFkit gem:
require "pdfkit"
kit = PDFKit.new(<<-HTML)
# CSS styles added...
<style>
table {
background-color: #ff33cc;
}
tbody tr:nth-child(odd) {
background-color: #ff33cc;
}
tbody tr:nth-child(even) {
background-color: #e495e4;
}
h1 {
text-align: center;
color: black;
margin-bottom: 100px;
}
.notes {
margin-top: 100px;
}
table {
width: 100%;
}
th {
text-align: left;
color: black;
padding-bottom: 15px;
}
</style>
<h1>My PDFkit Invoice</h1>
<table>
<thead>
<tr>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item #1</td>
<td>$27.00</td>
</tr>
<tr>
<td>Item #2</td>
<td>$100.00</td>
</tr>
</tbody>
</table>
HTML
kit.to_file("pdfkit_invoice.pdf")
Tip: Using a more advanced PDF library like the Prawn gem will give you even more styling options. See the Prawn manual to explore the gem's full capabilities.
Wrapping Up
In this article, we've highlighted how to generate PDF documents using three gems. We've also explored how to use the different gems to do things like add images to PDFs, send PDF attachments via email, and add basic styles to our documents.
This article is more of an outline than a deep dive. You're more than welcome to explore each gem's unique style of PDF generation, as well as how to do more complex documents than what we've covered here.
Happy coding!
Top comments (0)