Friday, June 14, 2013

WickedPDF, wkhtmltopdf, and Heroku...a tricky combination

At least for me as it took a bit of twiddling to get it running, first in development and then on the Heroku stack. But with a lot of testing and some help from unixmonkey and my friends at Thoughtbot, I finally got it working. Here's how.

First off, I'm running Rails 3.2.13, WickedPDF 0.9.6, and wkhtmltopdf 0.11.0 rc1, running development on a Linux (Kubuntu 10.04) 64-bit machine. NB: The machine you're developing on and deploying to can be different but then you'll have to have two (or more) different copies of wkhtmltopdf and properly configure WickedPDF to handle that difference. I cover that later in this post.

Install wkhtmltopdf

The download site for wkhtmltopdf has pretty much everything you need for whatever machine architecture you are working with. Download the matching flavor for your development and production machines. If you have only a single machine architecture, then just call your one binary 'wkhtmltopdf'.

If you're working with multiple architectures, label each one appropriately, either with the architecture suffix (e.g., -amd64) or simply '-dev' and '-prod'.

Place the one or more binaries in the directory "/bin", right off your root directory (e.g., "#{Rails.root}/bin") and mark them as executable (chmod +x on linux/mac). Note that for Windows development, you'll have a wkhtmltopdf.exe file for development and probably plain 'ol wkhtmltopdf for production.

Install WickedPDF

Installation is straight-forward; just follow the instructions here. However, there are some critical changes you must make for generating PDFs successfully. The section titled "Styles" gives you the key:
You must define absolute paths to CSS files, images, and javascripts
I'm highlighting this because I missed it the first time around and it cost me dearly.  This section goes on to suggest that you use the helpers that are provided. They are:
wicked_pdf_stylesheet_link_tag
wicked_pdf_image_tag 
wicked_pdf_javascript_include_tag 
Every external file that you use must be referenced with an absolute address, so the normal Rails helpers do not work in many cases [Technical Note: the wicked_pdf helpers simply embed the actual CSS or Javascript in the page]. To make life easier for me, I did the following:
  • Create a special pdf.html.haml layout for all PDF pages which uses the wicked_pdf helpers
  • Use the jQuery CDN for including that set of libraries; the Rails-provided helpers do work here because I'm providing an absolute URL; nothing relative to the Rails root of my application 
= javascript_include_tag "http://code.jquery.com/jquery-1.10.0.min.js"
= javascript_include_tag "http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"

Now here's the strange part [I believe this has to do with the Rails asset pipeline; I'll update this post once I nail that down]. I also had to add all my CSS and JS files to my application.rb file for inclusion in the asset pipeline, thusly:
config.assets.precompile += ['blueprint/screen.css', 'jquery.timepicker.css', ...]
Only with this incantation did everything work properly.

Configure WickedPDF for Development & Production

If you're developing on Linux 64-bit, then configuration is easy as you'll use the same binary for both development and production. Update your WickedPDF initializer file ("config/initializers/wicked_pdf.rb") as follows:

WickedPdf.config = { :exe_path => "#{Rails.root}/bin/wkhtmltopdf" }
If you're developing on something other than Linux 64-bit, you'll have to test for the environment and set your exe_path appropriately.. something like this:
WickedPdf.config do |config|  
  if Rails.env == 'production' then
    config.exe_path = Rails.root.to_s + "/bin/wkhtmltopdf"
  else  ### Following allows for development on my MacBook or Linux box
    if /darwin/ =~ RUBY_PLATFORM then
      config.exe_path = '/usr/local/bin/wkhtmltopdf' 
    elsif /linux/ =~ RUBY_PLATFORM then
      config.exe_path = '/usr/bin/wkhtmltopdf' 
    else
      raise "UnableToLocateWkhtmltopdf"
    end
  end
end

For windows, you'll have to check out the RUBY_PLATFORM values you see and modify appropriately; my windows machine reports "i386-mingw32'.

 If you need different versions of the same architecture (say for final testing a new version of wkhtmltopdf before rolling into production), then just label it with the version number and update your WickedPDF configuration file.

3 comments:

  1. Great post. Refreshing to see an updated workflow of this. Worked well for me.

    Thanks!

    ReplyDelete
  2. Thanks, Robert... Glad it helped you out!

    ReplyDelete
  3. Thanks for this. Mine only worked after changing the config.wkhtmltopdf in the PDFKit initializer to the result of the command 'which wkhtmltopdf'

    ReplyDelete