Configuring wkhtmltopdf on MacOS and Linux
I’ve been using wkhtmltopdf in Rails projects for years. After upgrading to Rails 6 and Ruby 2.6, PDF creation started failing for me. This post documents what I did to get it to work again.
Background
As of 12/4/2020, my deployment server is running Ubuntu 16.04.6 LTS, my development machine is running MacOS 10.15.7, and both are running ruby 2.6.6p146 & Rails 6.0.3.3. The relevant gems are pdfkit 0.8.4.3.2 & wkhtmltopdf-binary 0.12.6.5.
The original error message simply stated wkhtmltopdf failed with an exit status of 1.
Command failed (exitstatus=1): /.../versions/2.6.6/bin/wkhtmltopdf --quiet --page-size Letter --margin-top 4mm --margin-right 4mm --margin-bottom 27mm --margin-left 4mm --encoding UTF-8 --cookie <cookie name> <cookie data> --footer-html pdf_footer.html --footer-spacing 2 "http://localhost:<port>/.../report/1328" -
Although I’m using the pdfkit gem, all of the issues are with wkhtmltopdf, so they may be relevant to you even if you interface with wkhtmltopdf using some other means.
Issue 1: Unable to access local file containing footer html
I discovered the first issue was the inability to read the pdf_footer.html
file - see line 2:
1 2 3 4 5 6 7 8 9 10 |
options.reverse_merge!({ :cookie => { '<cookie name>' => session_cookie }, :footer_html => 'pdf_footer.html', :footer_spacing => 2, :margin_bottom => '27mm', :margin_left => '4mm', :margin_right => '4mm', :margin_top => '4mm'}) pdfkit = PDFKit.new(url, options) pdf = pdfkit.to_pdf |
The solution to this is to add the enable_local_file_access
option for wkhtmltopdf
(see line 2):
1 2 3 4 5 6 7 8 9 10 11 |
options.reverse_merge!({ :cookie => { '<cookie name>' => session_cookie }, :enable_local_file_access => true, :footer_html => 'pdf_footer.html', :footer_spacing => 2, :margin_bottom => '27mm', :margin_left => '4mm', :margin_right => '4mm', :margin_top => '4mm'}) pdfkit = PDFKit.new(url, options) pdf = pdfkit.to_pdf |
Issue 2: File permissions on linux
The next problem was related to file permissions on linux:
$ wkhtmltopdf --help
Traceback (most recent call last):
4: from /usr/local/bin/wkhtmltopdf:23:in `<main>'
3: from /usr/local/bin/wkhtmltopdf:23:in `load'
2: from /usr/local/lib/ruby/gems/2.6.0/gems/wkhtmltopdf-binary-0.12.6.5/
bin/wkhtmltopdf:55:in `<top (required)>'
1: from /usr/local/lib/ruby/gems/2.6.0/gems/wkhtmltopdf-binary-0.12.6.5/
bin/wkhtmltopdf:55:in `open' /usr/local/lib/ruby/gems/2.6.0/gems/
wkhtmltopdf-binary-0.12.6.5/bin/wkhtmltopdf:55:in `initialize':
Permission denied @ rb_sysopen - /usr/local/lib/ruby/gems/2.6.0/gems/
wkhtmltopdf-binary-0.12.6.5/bin/wkhtmltopdf_ubuntu_16.04_amd64 (Errno::EACCES)
I’m sure there’s a better way to handle this, I read accounts of needing to provide write access to the gem directory where wkhtmltopdf
lived, so:
cd /usr/local/lib/ruby/gems/2.6.0/gems/wkhtmltopdf-binary-0.12.6.5/bin
sudo chmod 777 .
Issue 3: Failing to authenticate properly
After fixing the above two issues, I was able to create PDF files of my report web page; however, the resulting PDF was of the login page, so apparently authentication was failing. Previously to upgrading ruby & Rails, it was sufficient to pass a cookie
option to wkhtmltopdf
(see line 1):
1 2 3 4 5 6 7 8 9 10 11 |
options.reverse_merge!({ :cookie => { '_<app-name>_session' => session_cookie }, :enable_local_file_access => true, :footer_html => 'pdf_footer.html', :footer_spacing => 2, :margin_bottom => '27mm', :margin_left => '4mm', :margin_right => '4mm', :margin_top => '4mm'}) pdfkit = PDFKit.new(url, options) pdf = pdfkit.to_pdf |
The session_cookie
value was obtained via cookies['_<app-name>_session']
in the controller. When I looked at the value of cookies['_<app-name>_session']
, I noticed it was different than the value stored in my browser’s cookie. However, that appears to be a red herring as there are reasons why Rails may return different values of encrypted cookies.
The root of the problem seems to be that the value of the cookie now needs to be escaped via: CGI.escape(cookies['_<app-name>_session'])
. After doing this, wkhtmltopdf was able to successfully authenticate.