HTML to PDF
in Ruby.

Most Rails apps generating PDFs still shell out to wkhtmltopdf through WickedPDF. It works until the design uses flexbox, or until the buildpack stops shipping the binary.

The call

Post your markup with an API key. The PDF comes back in the same response, base64 encoded by default. Nothing is written to disk on our side.

generate_pdf.rb
require "base64"
require "json"
require "net/http"

uri = URI("https://api.html4pdf.com/generate-pdf")

res = Net::HTTP.post(
  uri,
  {
    html: "<h1>Invoice INV-1042</h1><p>Total: $1,645.40</p>",
    options: { format: "A4", printBackground: true }
  }.to_json,
  "Authorization" => "Bearer #{ENV.fetch('HTML4PDF_KEY')}",
  "Content-Type" => "application/json"
)

data = JSON.parse(res.body)
File.binwrite("invoice.pdf", Base64.decode64(data["pdf"]))

From a Rails controller

Render the view you already have, post it, and send the bytes. No temporary files and no binary in your slug.

invoices_controller.rb
def show
  html = render_to_string(template: "invoices/show", layout: "pdf", formats: [:html])

  res = Net::HTTP.post(
    URI("https://api.html4pdf.com/generate-pdf"),
    { html: html, responseFormat: "binary" }.to_json,
    "Authorization" => "Bearer #{Rails.application.credentials.html4pdf_key}",
    "Content-Type" => "application/json"
  )

  send_data res.body, filename: "#{@invoice.number}.pdf", type: "application/pdf"
end

What comes back

The default response is JSON with the document base64 encoded, the credits you have left, and how long the render took. Send responseFormat as binary instead and the body is the PDF itself, with a Content-Type of application/pdf.

200 OK
{
  "success": true,
  "pdf": "JVBERi0xLjQKJeLjz9MKMyAwIG9...",
  "creditsRemaining": 9,
  "metadata": { "size": 42813, "processingTime": 2841 }
}

A 402 means you are out of credits, a 401 means the key is wrong, and a 422 means the markup could not be rendered. Everything else is worth retrying once.

The usual Ruby libraries

Worth knowing before you add a dependency, because for a lot of documents one of these is the right answer and costs nothing.

LibraryWhat it isThe trade-off
WickedPDFRenders a Rails view through the wkhtmltopdf binary.Familiar and Rails-native. You vendor a native binary per platform, and the engine behind it is unmaintained and pre-flexbox.
GroverDrives Puppeteer from Ruby.Chrome-accurate output, but it needs Node and Chrome alongside your Ruby app.
PrawnBuilds PDFs in Ruby with a drawing DSL.Precise and dependency-free. No HTML, so a layout change means writing code.

Ruby: common questions

How do I generate a PDF from a Rails view?

Use render_to_string to turn the view into HTML, post that string to a rendering API, and send_data the bytes back with a PDF content type. The controller above is the whole thing.

Is WickedPDF still a good choice?

It is fine for documents that stuck to simple CSS years ago. For new work, the unmaintained wkhtmltopdf binary underneath it is the problem, not the gem.

Does this work on Heroku?

Yes, and it removes the buildpack you currently need for wkhtmltopdf.

Can I use Faraday or HTTParty instead?

Yes. Any HTTP client works. Net::HTTP is used here only because it needs no gem.