New: Create and edit Word documents with DOCX Editor in WebViewer
By Apryse | 2018 Nov 29
6 min
Tags
ruby
generation
The best approach for generating PDFs in Rails really depends on the types of PDFs you need to generate. This tutorial provides step-by-step instructions for Rails PDF generation by using wkhtmltopdf
, an open source CLI for rendering HTML into PDF from standard Rails view and style code. This approach is ideal if you don't need a publishing workflow or precise control over page output.
For the purpose of this tutorial, I've made a simple demo application on Heroku to show you what we'll be building. The application lists a series of sample invoices that can be previewed in the browser, and then converted to PDF. The source code is available in our repo rails-generate-pdf
. Here's my configuration:
Rails 5.2.1
Postgresql 10.5
wkhtmltopdf 0.12.4
macOS Mojave 10.14.1
If you're looking for more granular control over PDF output, wkhtmltopdf
may not be the best. Instead, Prawn is a popular open source library that uses Ruby to generate PDFs. It is reasonably performant and uses a X,Y coordinate system for placing elements on a page. The downside is you'll need time to wrap your head around its rendering model, and learn its DSL (Domain Specific Language). Alternatively, you could use a commercial PDF generation library (like Apryse PDF SDK).
Let's begin!
Start by downloading and installing Wicked PDF's precompiled binary. You can test the output by converting a URL into a PDF file by opening the command line and typing this:
wkhtmltopdf http://google.com google.pdf
This will generate the file google.pdf
.
But, how do we use this in our Ruby on Rails projects?
Wicked PDF uses the shell utility wkhtmltopdf
to serve a PDF file from HTML. Rather than dealing with a PDF generation DSL of some sort, you simply write an HTML view as you would normally do, then let Wicked PDF take care of the hard stuff.
# command line - create Ruby on Rails project
rails new rails-generate-pdf –webpack=react –database=postgresql
We will use Bootstrap 4 for styling (which also requires jQuery) and wicked_pdf
for generating PDF. Here we'll install the required gems:
# Gemfile
# rails-generate-pdf/Gemfile
gem 'jquery-rails'
gem 'bootstrap', '~> 4.1.3'
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
Because wicked_pdf
is a wrapper for wkhtmltopdf
, we install that too (gem wkhtmltopdf-binary
). Once all gems are added to rails-generate-pdf/Gemfile
, run this from the root of your project:
# command line
bundle install
Then generate the initializer:
# command line
rails g wicked_pdf
For older Rails versions, you'll need to register the PDF MIME type in the initializer:
# file: rails-generate-pdf/config/initializer/mime_types.rb
Mime::Type.register “application/pdf”, :pdf
Our demo application will only have two models: Invoice and InvoiceItem. To create the Invoice model, navigate to the root of your project from the command line and type:
# command line -> generate Invoice Model
rails generate model Invoice from_full_name from_address from_email from_phone to_full_name to_address to_email to_phone status discount:decimal vat:decimal
Then open the Invoice model file (app/models/invoice.rb
) and add the following code:
# file: rails-generate-pdf/app/models/invoice.rb
class Invoice < ApplicationRecord
has_many :invoice_items, dependent: :destroy
STATUS_CLASS = {
:draft => "badge badge-secondary",
:sent => "badge badge-primary",
:paid => "badge badge-success"
}
def subtotal
self.invoice_items.map { |item| item.qty * item.price }.sum
end
def discount_calculated
subtotal * (self.discount / 100.0)
end
def vat_calculated
(subtotal - discount_calculated) * (self.vat / 100.0)
end
def total
subtotal - discount_calculated + vat_calculated
end
def status_class
STATUS_CLASS[self.status.to_sym]
end
end
Next, create the InvoiceItem model using the command line (in the root of your project):
# command line -> generate InvoiceItem Model
rails generate model InvoiceItem name description price:decimal qty:integer invoice:references
Now open the InvoiceItem model file (app/models/invoice_item.rb
) and add the following code:
# file: rails-generate-pdf/app/models/invoice_item.rb
class InvoiceItem < ApplicationRecord
belongs_to :invoice
end
Next, create your database and tables by running these commands:
# command line -> run commands
rake db:create
rake db:migrate
First generate the controller by running this command:
# command line -> run command
rails generate controller Invoices index show
Next, open your controller file (app/controllers/invoices_controller.rb
) and add the following code (which renders page contents into PDF format):
# file: rails-generate-pdf/app/controllers/invoices_controller.rb
class InvoicesController < ApplicationController
def index
@invoices = scope
end
def show
@invoice = scope.find(params[:id])
respond_to do |format|
format.html
format.pdf do
render pdf: "Invoice No. #{@invoice.id}",
page_size: 'A4',
template: "invoices/show.html.erb",
layout: "pdf.html",
orientation: "Landscape",
lowquality: true,
zoom: 1,
dpi: 75
end
end
end
private
def scope
::Invoice.all.includes(:invoice_items)
end
end
You'll notice we have two actions in our controller:
index
: shows a list of all invoicesshow
: previews a single invoiceThe show
action has two formats (html
and pdf
), which define how Rails renders the PDF content. For example, if you access the invoice directly without the .pdf
extension, you'll see an HTML preview, while adding the .pdf
extension renders it to PDF. Try it here:
You can configure the options however you need (see wkhtmltopdf
documentation for additional details). To customize the layout of the PDF, use the following settings:
invoices/show.html.erb
): defines the template to be used when rendering the pdfpdf.html.erb
): defines the main application layout(These files are available in our repo.)
Once you're happy with the layout, open your routes file (app/config/routes.rb
) and add the following code:
# file: rails-generate-pdf/app/config/routes.rb
Rails.application.routes.draw do
root to: 'invoices#index'
resources :invoices, only: [:index, :show]
end
First we need to create the new layout and use the helper from wicked_pdf
to reference the stylesheet, JavaScript, or image assets. For this example, I will only use the stylesheet helper:
# file: rails-generate-pdf/views/layotus/pdf.html.erb
<!DOCTYPE html>
<html>
<head>
<title>PDFs - Ruby on Rails</title>
<%= wicked_pdf_stylesheet_link_tag "invoice" %>
</head>
<body>
<%= yield %>
</body>
</html>
In this case, the css or scss file name is "invoice" (app/assets/stylesheets/invoice.scss
).
Now you need to make sure all the assets are precompiled (according to the official GitHub, this next step is essential for your gem to work perfectly when you deploy to production):
# file: config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( invoice.scss )
The above array must include every stylesheet and JavaScript library that you're using.
Next, we need to add a link to the invoice page to generate the PDF by pointing to the show
action of the invoice controller, and using the pdf
format (/invoices/:id.pdf)
):
# file rails-generate-pdf/app/views/layouts/shared/_header.html.erb
# invoice_path(@invoice, format: :pdf) -> /invoice/1.pdf
<%= link_to ‘DOWNLOAD PDF’, invoice_path(@invoice, format: :pdf) %>
And that’s it! You can now deploy your application for generating PDFs. Here's our demo application that we deployed to a free Heroku web dyno.
Here's how the index looks:
Here's how the PDF of the invoice looks like:
Using wkhtmltopdf
is a quick way to use the Ruby on Rails PDF generator. It’s easy to use Rails to convert HTML to PDF. However, if you need to generate PDFs from something other than HTML, want to integrate this into a workflow, or need precise control over positioning, then you're out of luck with wkhtmltopdf
.
Additionally, wkhtmltopdf
is based on the QtWebKit rendering engine, which is based on Apple's WebKit (which is a fork of KDE's KHTML). These engines are best suited for simple documents where you can afford to have some inconsistencies or failures.
If you need more reliability and robust functionality, Apryse’s Ruby PDF library offers PDF generation and many additional features. You can get started with a free trial and then check out some of the Ruby code samples to help get you up and running quickly.
If you have any questions about Apryse's PDF SDK, please feel free to get in touch!
You can find the source code for this blog post at our GitHub rails-generate-pdf
.
PRODUCTS
Popular Content