Working with PDFs in Ruby

Working with invoices, contracts, or just reading your favorite mystery thriller—whatever the case, PDF files are everywhere. Learn how to work with PDFs in Ruby.

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 ../config/initializers 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.

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, localhost:3000/orders/1.pdf, 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:

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 http://localhost:3000/rails/mailers/order_mailer/new_order to view the email preview (note the PDF attachment included in the email):

Email preview with attachment

Prawn PDF

Prawn PDF 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, 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:

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 mime_types.rb file and add the following line:

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!

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Aestimo Kirina

    Aestimo is a family guy, Ruby developer, and SaaS enterpreneur. In his free time, he enjoys playing with his kids and spending time outdoors enjoying the sun, running, hiking, or camping.

    More articles by Aestimo Kirina
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial
    “Wow — Customers are blown away that I email them so quickly after an error.”
    Chris Patton, Founder of Punchpass.com
    Start free trial