soli-pdf

Introduction

soli-pdf generates PDFs from Ruby. You describe a document's layout once, as a JSON template, and render it with a different data document each time: an invoice, a quote, a statement, a report.

require 'soli/pdf'

pdf = Soli::PDF.render(
  template: JSON.parse(File.read('invoice.template.json')),
  data: { 'invoice' => { 'number' => 'F-42' }, 'items' => items }
)
File.binwrite('invoice.pdf', pdf)

#Why a JSON template

Most Ruby PDF tools make you choose between two approaches:

  • Drawing APIs like Prawn. You get precise control, but the layout lives in Ruby code. Designers can't read it, and every change needs a deploy.
  • HTML to PDF through a headless browser or wkhtmltopdf. You write familiar markup, but you need a browser binary in production, pagination is hard to control, and rendering takes hundreds of milliseconds.

soli-pdf takes a third approach: the layout is data. A template is a JSON document made of elements such as paragraphs, tables, boxes, charts, QR codes and repeat blocks. The renderer lays them out, paginates them and writes the PDF. This gives you three things:

  • Templates are portable. They live in your repository, diff cleanly, and can be generated, validated or edited by any tool, not only by Ruby.
  • Data and layout stay separate. The template holds ${invoice.number} and the data holds "F-42". Your models build a plain Hash, and the template decides how it looks.
  • The output is a real document. Tables repeat their header rows on every page. Totals can be carried forward. Page numbers are resolved after pagination. You can produce Factur-X e-invoices, tagged accessible PDFs, encrypted PDFs, and documents with attachments.

#How it works

The gem is a thin Ruby layer, under 300 lines with no runtime dependencies, over render_pdf, a native renderer written in Rust.

  1. Soli::PDF.render serialises your template and data to JSON.
  2. It starts render_pdf. The template goes through a temporary file, the data travels on standard input, and the PDF comes back on standard output.
  3. You get the PDF as a binary String. Warnings, such as a missing glyph or a skipped image, are collected in Soli::PDF.last_warnings. A failed render raises Soli::PDF::RenderError.

The binary is not bundled in the gem. The first time you render, the gem downloads the right build for your platform from the GitHub release, checks it against the published SHA-256, and caches it. The render_pdf binary explains how the binary is found, and how to supply your own.

#What you can build

NeedWhere to look
Invoices, credit notes, receiptsExamples: Billing
Line items that paginate, subtotalsTables
Grouped or conditional contentData binding
Charts from your figuresCharts
SEPA scan-to-pay QR codes, barcodesQR codes & barcodes
French / EU e-invoicing (EN 16931)Factur-X e-invoices
Password-protected output, letterhead, attachmentsRender options
Accessible PDFs for screen readersTemplate structure

#Where to go next

  • Installation: add the gem and check that the binary resolves.
  • Quickstart: your first PDF in five minutes.
  • Examples: sixteen documents with their templates, data and Ruby code.
  • Playground: edit a template in the browser and see the pages.