Skip to content
Back to blog

HTML to PDF in Ruby on Rails

Explore modern solutions for converting HTML to PDF in Rails applications using Ruby-Puppeteer, moving beyond legacy tools like WickedPDF and Prawn.

HTML to PDF conversion illustration

Converting HTML to PDF has long been a staple in web development, particularly for applications that need to generate invoices, reports, or any printable content. Traditionally, the Rails community has relied on gems like WickedPDF and Prawn. However, these tools have their limitations, and newer, more robust solutions like Puppeteer have emerged. In this post, I'll walk you through the current landscape and show you how to use the ruby-puppeteer gem to convert HTML to PDF in your Rails application.

Legacy Solutions

WickedPDF has long been a popular choice for developers, utilizing the wkhtmltopdf command-line tool, which employs Webkit to render HTML and CSS. However, the maintenance and updates for WickedPDF have dwindled, raising concerns about its long-term sustainability. While it worked for some legacy projects, we faced significant challenges trying to get it functional again during our Ruby/Rails upgrade processes. With the wkhtmltopdf repository being archived and no updates in nearly five years, we had to question whether it was worth the effort to continue using it or if it was time to look for an alternative solution.

Prawn is another popular gem in the Rails ecosystem, known for its flexibility and powerful PDF generation capabilities. However, Prawn isn't designed to convert HTML to PDF. Instead, it provides a programmatic API to create PDFs from scratch, which can be time-consuming and less intuitive for those who prefer to work directly with HTML and CSS. We already had the HTML and CSS working in our project and wanted to find the "path of least resistance" to fix this relatively trivial problem from an outside perspective.

Enter Ruby-Puppeteer

After extensive research, I aimed to find a solution using Puppeteer. Initially, I developed a Node.js-based approach that involved spinning up a browser pool to handle incoming requests. However, this approach turned out to be too cumbersome to deploy, secure, and set up in our development environments.

I then discovered Ruby-Puppeteer. Ruby-puppeteer is a gem that wraps the functionality of Puppeteer, a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It can render HTML and CSS using the latest web standards, making it a powerful tool for converting HTML to PDF.

Here's how you can integrate ruby-puppeteer into your Rails application:

Setting Up Ruby-Puppeteer

First, you'll need to install the ruby-puppeteer gem. Add it to your Gemfile and bundle install:

# Gemfile
...
gem 'puppeteer-ruby'
...

Run the bundle command to install the gem:

bundle install

Creating a Service to Generate PDFs

Next, create a service in your Rails app to handle the PDF generation. You can place this service in app/services and name it pdf_generator_service.rb.

# app/services/pdf_generator_service.rb
require 'puppeteer-ruby'

class PdfGeneratorService
  def initialize(html_content)
    @html_content = html_content
  end

  def generate_pdf
    tempfile = Tempfile.new(['document', '.pdf'])

    Puppeteer.launch do |browser|
      page = browser.new_page
      page.set_content(@html_content)
      page.pdf(path: tempfile.path, format: 'A4')
    end

    File.read(tempfile.path)
  ensure
    tempfile.close
    tempfile.unlink
  end
end

Using the Service in Your Controller

Now, you can use the PdfGeneratorService in your Rails controller to generate PDFs from HTML content.

# app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
  def create_pdf
    html_content = render_to_string(template: 'documents/show', layout: false)
    pdf_content = PdfGeneratorService.new(html_content).generate_pdf

    send_data pdf_content, filename: 'document.pdf', type: 'application/pdf'
  end
end

Drawbacks of Using Ruby-Puppeteer

While Ruby-Puppeteer is powerful, it's not without its challenges:

  1. Dependency on Node.js: Unlike Ruby gems, Puppeteer requires Node.js, adding another layer to your tech stack.
  2. Resource Intensive: Puppeteer runs a full instance of Chromium, which can be resource-intensive, especially under heavy load.
  3. Deployment Complexity: Ensuring that Puppeteer and Chromium are correctly installed and running on your production environment can be tricky. Docker can simplify this, but it adds complexity.

Conclusion

Switching from traditional tools like WickedPDF and Prawn to Ruby-Puppeteer can offer more flexibility and modern web standards support for converting HTML to PDF in Rails. While there are some drawbacks, the benefits of using a robust and actively maintained tool like Puppeteer can be significant, especially for complex PDF generation needs.