pdg XML → PDF

Guide

Rails

The pdg-rails gem — render PDFs in Rails like HTML, with .pdf.erb views, layouts, and partials.

pdg-rails lets you render PDFs the way you render HTML: write .pdf.erb views and render them through the normal Action View stack — layouts, partials, helpers, any template engine — to produce a pdg XML document, which the pdg gem turns into a PDF.

<%# app/views/layouts/pdf.pdf.erb %>
<document version="2.0">
  <config><page size="A4" margin="40px"/></config>
  <pages><page><%= yield %></page></pages>
</document>
<%# app/views/invoices/show.pdf.erb %>
<text size="20" bold="true"><%= @invoice.title %></text>
<%= render @invoice.line_items %>
<text>Total: <%= number_to_currency(@invoice.total) %></text>
class InvoicesController < ApplicationController
  def show
    @invoice = Invoice.find(params[:id])
    respond_to do |format|
      format.html
      format.pdf
    end
  end
end

GET /invoices/1.pdf now returns a PDF.

Installation

# Gemfile
gem "pdg-rails"
bundle install

This pulls in the pdg gem, which bundles the native engine — no toolchain needed.

Two ways to render

Both run the view through the full Action View stack.

The :pdf format. Register a .pdf.erb view and let the format do the work: respond_to { |format| format.pdf }, or simply visit /invoices/1.pdf with no respond_to block — the implicit render is intercepted when the request format is :pdf. The download filename defaults to the action (show.pdf) and the disposition to inline.

render pdf:. For explicit control — a different template, a layout, a download name:

render pdf: "invoices/show",          # template (defaults to the current action)
       layout: "pdf",                 # layout name, or false for none
       locals: { invoice: @invoice },
       filename: "invoice-#{@invoice.number}.pdf",
       disposition: "attachment",     # force a download
       base: Rails.root.join("app/pdf_assets").to_s

Layouts, partials, helpers

Everything works as it does for HTML:

  • Layouts live at app/views/layouts/<name>.pdf.erb and wrap the view with <%= yield %>. The application layout is used by default if a .pdf variant exists; name another with layout: or PdgRails.config.layout.
  • Partials render with render "partial", render @collection, etc. — name them _line_item.pdf.erb.
  • Helpers and instance variables are all in scope. Any handler works: show.pdf.haml, show.pdf.slim — it just has to output pdg XML.

The composed document must be valid pdg XML — see the element reference and the other guides.

Assets

Relative paths in the document (<font-face src>, <image src>, <pdf src>) resolve against base, which defaults to Rails.root. http(s) URLs and <google-font> faces are fetched by the engine regardless.

Errors

A failed render raises from the engine gem: PDG::RenderError when the document is withheld (a strict-mode block or a fatal parse/layout error), carrying #diagnostics. Unhandled, it surfaces as a 500 like any controller exception — rescue it to report the diagnostics or render a fallback.

rescue_from PDG::RenderError do |e|
  Rails.logger.error(e.message)   # includes each diagnostic
  head :unprocessable_entity
end

Configuration

# config/initializers/pdg.rb
PdgRails.configure do |c|
  c.layout = "pdf"            # default layout for PDF views (nil = Rails default)
  c.disposition = "inline"    # "inline" or "attachment"
  c.base = Rails.root.join("app/pdf_assets").to_s
  c.intercept_format = true   # auto-render the :pdf format; false = require `render pdf:`
end