DEV Community

loading...

Converting HTML to PDF using Rails

Ayush Newatia
I'm a freelance web developer and Rubyist. I also run Chapter24.app on the side!
・3 min read

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

Discussion (1)

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.

Forem Open with the Forem app