DEV Community

Ayush Newatia
Ayush Newatia

Posted on

Converting HTML to PDF using Rails

Exporting to PDF from HTML can be a bit of a can of worms, especially with CSS not quite working the way it does in a web browser. However with the right setup, it's possible to take the pain out of it!

A couple of popular gems to convert HTML to PDF in Rails are PDFKit and WickedPDF. They both use a command line utility called wkhtmltopdf under the hood; which uses WebKit to render a PDF from HTML.

I'd highly advise against using both those gems. They're good libraries but the underlying wkhtmltopdf doesn't support modern CSS features such as custom properties or grid; so you might find yourself unable to use any of the existing CSS in your app to style your PDF export.

The gem I recommend is called Grover. It uses Puppeteer and Chromium to "print" an HTML page into a PDF. So your PDF will look exactly how your page looks in Google Chrome's print preview. This will also allow you to reuse CSS from your app rather than having to write specific CSS just for your PDF exports.

Since Grover uses Chromium which runs external of your Rails app, you need to reference all your assets with absolute paths instead of relative paths. The easiest way to enable this is to set config.asset_host in your app configuration. This ensures the stylesheet_link_tag  and font-url helpers output the absolute path including your domain name rather than just the relative path.

Depending on the complexity of your requirements, you might want to set up Grover as a middleware. You can read up on how to do that in their comprehensive Readme. However if all you're trying to do is allow a user to download a dynamically generated PDF, the below controller code is all that's needed!

def render_pdf(html, filename:)
  pdf = Grover.new(html, format: 'A4').to_pdf
  send_data pdf, filename: filename, type: "application/pdf"
end
Enter fullscreen mode Exit fullscreen mode

 

I put this method in a Concern so I can include it in any controller I need to. I'd also recommend creating a new layout for your PDFs as you likely won't need all the markup your application.html.erb includes.

Here's an example of a controller action that generates and triggers a download of a PDF for an invoice:

 

def download
  invoice = render_to_string "download.html.erb", layout: "pdf"

  respond_to do |format|
    format.html { render html: invoice }
    format.pdf { render_pdf invoice, filename: t(".filename", id: @invoice.id) }
  end
end
Enter fullscreen mode Exit fullscreen mode

 

Having both HTML and PDF formats for this action enables a quick feedback loop during development. It allows you to view the HTML page in your browser and then test the PDF export once you have the basics down. 

Exporting to PDF doesn't have to be a pain thanks to Grover! You might still find some quirks with your CSS so you might have to create a separate CSS bundle for PDFs; however the vast majority of your CSS should "just work".

This post was originally published on my blog

Top comments (5)

Collapse
 
renaud profile image
Renaud 🤖

With wkhtmltopdf not maintained anymore, I think Puppeteer is the best way to do HTML to PDF. But beware it can be tricky to scale your pdf exports

Collapse
 
camilaulmetepais profile image
Camila Ulmete Pais

Hey there! Thank you for this article, it was really useful since there isn't much on the internet about implementing Grover.
I was wondering if you might have any idea why the server isn't outputting a file. It's not raising any exceptions, but it doesn't recognise the format.pdf bit.
Any advice is helpful. Thank you again!

Collapse
 
ayushn21 profile image
Ayush Newatia

Hey ... sorry I can't think of any likely issue off the top of my head I'm afraid ... it could literally be anything :(.

If you can email me a link to a GitHub repo with the issue I'll try to take a peek when I get some time, but might be a week or two before I get round to looking at it.

ayush (at) radioactivetoy (dot) tech.

Collapse
 
camilaulmetepais profile image
Camila Ulmete Pais

Yeah, I figured... I'm in way over my head anyway, as a junior this is a bit too much for me haha
Sadly the code isn't mine to share, since it belongs to the company I work for. My senior colleague will be taking over, hopefully he'll be able to make it work.
Thank you so much for the offer anyway. Take care!

Collapse
 
cescquintero profile image
Francisco Quintero 🇨🇴

Hey, thanks for sharing. It's nice to know there are other alternatives to WickedPDF. Yeah, sadly support for modern CSS in WKHTMLTOPDF is very poor but it's still useful for simple styling.