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:
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):
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:
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):
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.