babvijayb

Joined

50 Experience
0 Lessons Completed
0 Questions Solved

Activity

Background

I have an existing Rails 5.0.7.2 app running on Ruby 2.5.1. In several places I generate invoices as HTML (I build a full HTML string by replacing template tags server-side) and convert to PDF for download.

Problem

WickedPDF (wkhtmltopdf) is currently used. It generally works, but wkhtmltopdf's old WebKit engine doesn't support modern CSS (flexbox / grid) reliably. I replaced many layout bits with tables/floats to try to make it stable, but when invoice content gets long the PDF layout still breaks (columns jump, bottom section moves to next page, etc).

I want a modern engine that renders the same HTML/CSS as Chrome (so flex/grid work and page-breaks behave like in the browser). But my app uses Ruby 2.5.1 and Rails 5.0.7.2, so many modern gems are not compatible.

What I tried (summary)

  1. WickedPDF + wkhtmltopdf

Works with Ruby 2.5.1.
Pros: no Ruby upgrade required, already in project.
Cons: old WebKit — flex/grid / page-breaks unreliable.

  1. Grover (Puppeteer/Headless Chrome wrapper)

Goal: use headless Chrome so rendering matches browser.
Issues encountered:
Grover::DependencyError: Cannot find module 'puppeteer' → installed puppeteer via npm.
Chromium failed to run due to OS libraries (libnspr4.so missing) — fixed by installing system packages (Ubuntu 24.04 t64 libs).
PATH issues inside Rails (Spring/asdf) — fixed by exporting asdf shims into Rails env.
Final blocking error: NoMethodError: undefined method 'pack' for nil:NilClass from grover/processor.rb. Investigating versions revealed Grover >= 1.1 requires Ruby ≥ 2.7 and newer Grover versions are incompatible with Ruby 2.5. Attempts to find a compatible Grover version led to version constraints or runtime errors (pack nil or other incompatibilities). Conclusion: Grover is not a viable option unless I upgrade Ruby.

  1. prawn + prawn-html

Prawn itself is fine on Ruby 2.5.1 but prawn-html requires Ruby >= 2.6, so it won't install in my environment. Using raw Prawn would require me to rewrite the whole invoice layout in Prawn DSL (workable but heavy).

  1. pdfkit / wkhtmltopdf

Same underlying engine as WickedPDF, so it doesn't solve the CSS/flexbox problem.

  1. Tried multiple Grover versions

Older 0.x or 1.x variants either lack needed capabilities or require newer Ruby. The project’s Ruby limits all modern Grover versions.

My constraints

  • I cannot upgrade Ruby/Rails right now (legacy app + constraints).
  • I want to keep using the generated HTML (no time to rewrite invoice into Prawn DSL).
  • Prefer a solution that: a) renders modern CSS (flex/grid), b) works without forcing a Ruby upgrade, or c) provides an easy migration path.

What I'm asking

  1. Is there any Ruby gem that:

accepts an HTML string and converts to PDF using a modern browser engine (Chrome) or otherwise supports flex/grid and modern CSS, and
is compatible with Ruby 2.5.1 / Rails 5.0.7.2?

  1. If no gem fits, what are practical alternatives that integrate cleanly with a Rails 5 + Ruby 2.5 app and let me preserve my HTML templates? Examples I’m thinking of:

Running a small Node microservice (Puppeteer) that accepts HTML and returns PDF — examples or starter repos?
Using a 3rd-party API service (CloudConvert, HeadlessChromium.as-a-service, etc). Any recommendations for self-hosted / low-cost?
Strategies to safely upgrade Ruby to 2.7+ for this app (rough checklists, gotchas for Rails 5.0.7.2)?

  1. If you’ve solved multi-page invoice layout issues with wkhtmltopdf, what CSS/practical fixes worked for you (page-break-* rules, avoiding table-cells across pages, float hacks)? I’ve tried:

page-break-inside: avoid on dynamic blocks
converting bottom two-column table into floating <div> columns with clear:both
limiting overflow-x and avoiding CSS grid/flex in PDF mode
but still got unpredictable full-column jumps when left column content is very large.

Appendix — key code snippets & errors

Controller (current):

def download_invoice
invoice = Invoice.find_by_id(params[:invoice_id])
pdf = generate_pdf(
@is_blur ? invoice.try(:invoice_html_blur, for_pdf: true) : invoice.try(:invoice_html, for_pdf: true)
)
send_data pdf, filename: "Invoice.pdf"
end

def generate_pdf(invoice_html)
WickedPdf.new.pdf_from_string(
invoice_html.encode('UTF-8', invalid: :replace, undef: :replace),
encoding: 'UTF-8',
footer: {
center: "Generated PDF",
right: "Page [page] of [topage]",
font_size: 8,
spacing: 2
},
dpi: 300
)
end