Send PDF attachments from Rails with WickedPdf and ActionMailer

2 minute read

In almost any web application you create, the question of generating PDF files will pop up pretty soon. There are a couple of options while using Ruby on Rails. First one is Prawn which is a pure PDF generator library, and the other options are mostly wrappers around the very popular wkhtmltopdf unix library that converts html pages to pdf. There is also an option that uses PrinceXML but I would initially exclude it because it is pretty pricey for an SME.

While using Prawn gives you all of the formatting power when creating PDF files, it’s learning curve is pretty steep, and the option of converting html to a PDF seems pretty good. This is especially the case when you already have html templates that only need some small modifications to be able to convert them to PDF.

There are a couple of gems that wrap around the wkhtmltopdf library, and ruby-toolbox pdf generation section shows some more viable options that can be used when generating PDF files from ruby.

I’ll be using WickedPdf in this example, as that is the one I have set up this system with a couple of times already, so I’m pretty confident it will work in any other Rails application.

Setup

As always, using a ruby gem in rails is pretty simple, you just add a couple of lines to the Gemfile

gem 'wicked_pdf'
# we need the new binary here, so that we can be OS independent
gem 'wkhtmltopdf-binary', github: 'pallymore/wkhtmltopdf-binary-edge', tag: 'v0.12.2'

Usage

This setup will work pretty straightforward in the controllers, because WickedPdf registers :pdf request format, and you can respond to it in the same fashion as html or js in a respond_to block. Code below is copied from the WickedPdf Readme page.

class ThingsController < ApplicationController
  def show
    respond_to do |format|
      format.html
      format.pdf do
        render pdf: "file_name" # Excluding ".pdf" extension.
      end
    end
  end
end

That part was pretty easy, and you can configure the pdf render with a lot of options that are mentioned in the advanced configuration section of the WickedPdf Readme

Generating PDF from ActionMailer

The issue when trying to generate the PDF from ActionMailer is that there is no request, so you have to render the html in a different way. In a nutshell, you want to render the template to a string, and then forward that string (html) to the wkhtmltopdf. This is the same way the gem work when rendering pdf from the controller, but it is worth mentioning once more.

We will use a method that exists on the AbstractController::Rendering and it is called render_to_string So we can do something like this in the mailer:

class TodoMailer < ActionMailer::Base
  #...
  def pdf_attachment_method(todo_id)
    todo = Todo.find(todo_id)
    attachments["todo_#{todo.id}.pdf"] = WickedPdf.new.pdf_from_string(
      render_to_string(pdf: 'todo', template: 'todo.pdf.erb', layout: 'pdf.html'), { :hash_with_wickedpdf_options }
    )
    mail(to: todo.owner.email, subject: 'Your todo PDF is attached', todo: todo)
  end
end

By using this approach, we can easily send a PDF attachment from an ActionMailer method. You can do this for any Rails template you want, but be sure to test everything before putting it into production.

Comments