Generate PDF in Ruby on Rails using Wicked PDF

March 8, 2018
admin

One of the fastest ways to generate a PDF in Ruby on Rails is to use the wicked_pdf gem. rubygems.org is the go-to station for all rails developers in search of any gems. wicked_pdf uses a shell utility wkhtmltopdf, which allows you to simply write an HTML view that gets converted into a PDF file. Let’s get started with the following steps.

Installation

Firstly, navigate to your project working directory then copy the following lines in your Gemfile and run ‘bundle install’ from the terminal.

gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
gem 'wkhtmltopdf-binary-edge'

Add or modify config/initializers/mime_types.rb file to register the PDF mime type.

Mime::Type.register "application/pdf", :pdf

Now run following command to generate initial configurations.

rails generate wicked_pdf

This will create wicked_pdf.rb under config/initializers.

Setup

Now the controller needs to be set up for rendering the PDF. Add following respond_to block in your controller action method that will generate the PDF view.

respond_to do |format|
 format.html
 format.pdf do
   render template: 'companies/show', pdf: 'Company Detail Report-' + Time.now.strftime('%v %H:%M:%S').to_s, javascript_delay: 10000,
   layout: 'pdf_layout.html.haml', disposition: 'attachment'
 end
end

Specifying the disposition as ‘attachment’ will download the page as PDF and setting it as ‘inline’ will simply generate a PDF in a new tab.

Make a separate layout file, which the PDF view will use. For example, here pdf_layout.html. HTML is created in the layouts folder. As wicked_pdf cannot identify assets pipeline, the HTML structure needs to be added to the layout HTML file.

!!!
%html{ lang: 'en' }
 %head
 %body
   = yield

In order to access any custom CSS or bootstrap class, add the following helpers to the head in the layout file.

= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
= wicked_pdf_stylesheet_link_tag 'dashboard', media: 'all', 'data-turbolinks-track' => true
= wicked_pdf_stylesheet_link_tag('bootstrap')

You can add assets CSS and Javascript in application.css and application.js files.

Application.js:

//= require highcharts
//= require highcharts/highcharts-more
//= require turbolinks
//= require bootstrap-sprockets
//= require bootstrap-select

Application.css :

@import 'bootstrap-sprockets';
@import 'bootstrap';
@import 'bootstrap-select';

The extension of the view file should be as such, .pdf.haml.
Add the link to generate the PDF file as follows:

= link_to companies_path(@company, format: :pdf), target: '_blank', title: 'Click to Export as PDF', class: 'btn btn-default' do
 %i.glyphicon.glyphicon-download-alt
Export as PDF

Graph generation

The project I was working on required several interactive graphs, which I further needed to display in my PDF viewer. I used a javascript framework Highcharts, which I integrated with Wicked PDF  Here’s an easy way to render Highcharts graph in your PDF. Firstly, add the followings in your layout file.

%script{:src => "https://code.highcharts.com/highcharts.js"}
%script{:src => "https://code.highcharts.com/highcharts-more.js"}
%script{:src => "https://code.highcharts.com/modules/exporting.js"}

Next in pdf.haml, where the graph will be rendered, simply add the javascript path where you have created the graph. The format and a javascript delay need to be added, along with any necessary parameters that need to be passed to create the graph. The javascript delay denotes the time taken to load the graph.

= render partial: 'companies/pie_chart, formats: [:html], :javascript_delay => 10000, locals: { gross_profit: @gross_profit}

While rendering the chart, elements like animation may delay it to the extent of not rendering the graph in time. This is an optional step, but just to be safe the followings could be added to the file containing the charts.

plotOptions: {
 series: {
  enableMouseTracking: false,
  shadow: false,
  animation: false
 }
}

Page Number

Numbering pages is an important aspect of any PDF. In the ‘onload’ of the body in the PDF layout, call number_pages() function. The js function of number_pages(), will contain the following:

function number_pages() {
 var vars={};
 var x=document.location.search.substring(1).split('&');
 for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
 var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
 for(var i in x) {
  var y = document.getElementsByClassName(x[i]);
  for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
 }
}

To add the page number as footer add it to your controller as shown below. Its size can be changed with: font_size element.

render template: 'companies/show', pdf: 'Company Detail Report-' + Time.now.strftime('%v %H:%M:%S').to_s, javascript_delay: 10000,
 layout: 'pdf_layout.html.haml', disposition: 'attachment', viewport_size: '1280x1024', :footer => { :right => 'Page [page] of [topage]', :font_size => 7 }

Displaying an Image

To insert an image in PDF, one must use the pdf_image_tag helper. Adjust the height and width of the image accordingly. The images can be kept conventionally inside assets/images folder.

wicked_pdf_image_tag( 'growth.png', height: '15', width: '15')

Page Break

To force a page break before a new page adds the following CSS in your CSS file:

.page-break {
   display:block; clear:both; page-break-before:always;
 }

Just change to page-break-after: always; for enforcing page break at the end of a page.

Now you’ll have a PDF generated from a rails view up and running, with some common but essential features!

Contributor: Mehreen MansurNascenia

2 Comments. Leave new

Bilal Abbas
June 17, 2019 6:17 pm

What about image url for wicket_pdf_image_tag
I am assign simple http://www.rspcansw.org.au/wp-content/themes/noPhotoFound.png url to wicket_pdf_image_tag but it is not working at all.
any idea about that??

Reply

use this tag wicked_pdf_image_tag to get the correct output.
Let us know if it worked for you.

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.