Generate PDF files from your Rails app

Working recently on a Rails project, I needed to generate PDF files from the app. After searching on Github (specifically in Awesome Ruby) for open source solutions that fit my needs , I ended up choosing wicked_pdf. Let’s see how to make the magic happen.

Why wicked_pdf ?

I chosen Wicked PDF for 3 specific reasons,

  • It’s very easy to use
  • It helps you generate your PDF files from HTML & CSS (& JS)
  • It’s easy to integrate into a Rails project (It’s actually a Rails plugin)

Wicked_pdf uses the awesome wkhtmltopdf project, to create PDF files. The wkhtmltopdf itself, is a command line tool to render HTML into PDF using the QT webkit rendering engine.

Install

wicked_pdf

Add this to your Gemfile, then run  bundle install :

gem 'wicked_pdf'

wkhtmltopdf

Because wicked_pdf is a wrapper of wkhtmltopdf, you’ll need to install it too. You can install it through the wkhtmltopdf-binary by adding this to your Gemfile and running bundle install:

gem 'wkhtmltopdf-binary'

For one reason or another you may want to install it manually instead. In that case, you will have to tell wicked_pdf where the executable is located. Run rails g wicked_pdf to generate an initializer for wicked_pdf and then add:

WickedPdf.config = {
  exe_path: '/usr/local/bin/wkhtmltopdf' # The value of exe_path being the path to the wkhtmltopdf binary
}

Render a view as pdf

To render a  show.html.erb view as pdf, we’ll simply do:

class MyController < ApplicationController
  def show
    format.pdf do
      render pdf: 'file_name'   # Excluding ".pdf" extension.
    end
  end
end

Because the wkhtmltopdf binary works outside of the rails app, it won’t be able to load our app assets if the view (or the view’s layout) includes any. Let’s fix that by creating a special layout for our PDF files. Assets can be included into the layout either using wicked_pdf helpers or CDN references.

Create layout using wicked_pdf helpers

wicked_pdf makes available 3 helpers: wicked_pdf_stylesheet_link_tag, wicked_pdf_javascript_tagwicked_pdf_image_tag that we can use to include our assets as one would do with the Rails asset pipeline methods.

//pdf_layout.pdf.erb

<!doctype html>
<html>
  <head>
    <meta charset='utf-8' />
    <%= wicked_pdf_stylesheet_link_tag "app_css_file" -%>
    <%= wicked_pdf_javascript_include_tag "app_js_file" %>
  head>
  <body>
    <div id="header">
      <%= wicked_pdf_image_tag 'pdf_header_image.jpg' %>
    div>
    <div id="content">
      <%= yield %>
    div>
  body>
html>

Create layout using CDN references

In the case that the assets files are libraries or frameworks available on CDNs, we can simply use the Rails asset tag helpers (stylesheet_link_tagjavascript_include_tagimage_tag) to include them.

//pdf_layout.pdf.erb

<!doctype html>
<html>
  <head>
    <meta charset='utf-8' />
    <%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" -%>
    <%= javascript_include_tag "https://code.jquery.com/jquery-3.2.1.min.js" %>
  head>
  <body>
    <div id="header">
      <%= image_tag "https://placehold.it/350x150" %>
    div>
    <div id="content">
      <%= yield %>
    div>
  body>
html>

Rendering with the layout

class MyController < ApplicationController
  def show
    format.pdf do
      render pdf: 'file_name',
             layout: 'pdf_layout' # for a file named pdf_layout.pdf.erb
    end
  end
end

Advanced usage

If you would like to create a PDF file and make another use of it instead of directly rendering it, or if you want to control page breaks and page numbering, read the  project’s README advanced usage section.

Other issues

CSS Flexbox not supported

I found out that, at the time of this writing, the version of Qt Webkit used in the wkthmltopdf binary doesn’t support the CSS Flexbox specification. To take advantage of Flexbox in your PDF files, I recommend using a polyfill as the awesome flexibility library  in your views.

PDF files generation time

Depending on the contents involved, the server can take a long time to generate a PDF file. Thus, it’s better to put the generation logic into a job instead of the request/response cycle. See the Ruby on Rails guide to learn more about jobs.

Thanks for reading !

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s