soli-pdf

Minimal invoice

One number at 46 pt, everything else at 8 pt. The hierarchy is carried by the type scale alone.

Two rules on the whole page and no accent colour. The margins are widened to 64 pt, which narrows the content width to 467 pt: every column width in the file adds up to that.

Custom margins Typographic hierarchy 1 page
Page 1
Page 1 of 1
render_invoice_minimal.rb ruby
require 'json'
require 'soli/pdf'

template = JSON.parse(File.read('invoice_minimal.template.json'))
data     = JSON.parse(File.read('invoice_minimal.data.json'))

pdf = Soli::PDF.render(
  template: template,
  data: data,
  title: 'Invoice 2025-14'
)
File.binwrite('invoice_minimal.pdf', pdf)

Run it yourself

  1. Add the gem to your Gemfile — see Installation.
  2. Download invoice_minimal.template.json and invoice_minimal.data.json.
  3. Run ruby render_invoice_minimal.rb.
invoice_minimal.template.json json
{
  "fonts": ["titillium"],
  "options": { "header_height": 0, "margins": 64 },
  "content": [
    {
      "type": "table",
      "header_columns": [
        {
          "text": "${seller.name}",
          "width": 240, "fontSize": 9, "fontWeight": "bold", "alignment": "left",
          "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" }
        },
        {
          "text": "Invoice ${invoice.number}",
          "width": 227, "fontSize": 9, "alignment": "right",
          "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" }
        }
      ],
      "rows": [],
      "options": { "header": { "textColor": "111111" }, "padding_x": 0, "padding_y": 0 }
    },

    { "type": "move", "y": 96 },

    { "type": "paragraph", "value": "AMOUNT DUE", "options": { "fontSize": 8, "fontWeight": "bold", "color": "8A8A8A" } },
    { "type": "move", "y": 6 },
    { "type": "paragraph", "value": "${totals.due}", "options": { "fontSize": 46, "fontWeight": "bold", "color": "111111" } },
    { "type": "move", "y": 4 },
    { "type": "paragraph", "value": "by ${invoice.due}", "options": { "fontSize": 11, "color": "555555" } },

    { "type": "move", "y": 26 },
    { "type": "hr", "color": "111111", "thickness": 1 },
    { "type": "move", "y": 20 },

    {
      "type": "table",
      "data": "items",
      "header_columns": [],
      "rows": [
        [
          { "text": "${name}",   "width": 367, "fontSize": 9.5, "alignment": "left",  "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" } },
          { "text": "${amount}", "width": 100, "fontSize": 9.5, "alignment": "right", "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" } }
        ]
      ],
      "options": { "padding_x": 0, "padding_y": 5 }
    },

    { "type": "move", "y": 14 },
    { "type": "hr", "color": "D4D4D4", "thickness": 0.5 },
    { "type": "move", "y": 10 },

    {
      "type": "table",
      "header_columns": [],
      "rows": [
        [
          { "text": "Net ${totals.net}   ·   VAT ${invoice.vat_rate} ${totals.vat}", "width": 367, "fontSize": 8.5, "alignment": "left", "color": "6B6B6B", "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" } },
          { "text": "${totals.due}", "width": 100, "fontSize": 10, "fontWeight": "bold", "alignment": "right", "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" } }
        ]
      ],
      "options": { "padding_x": 0, "padding_y": 0 }
    },

    { "type": "move", "y": 296 },

    {
      "type": "table",
      "header_columns": [],
      "rows": [
        [
          {
            "content": [
              { "type": "text", "value": "PAY TO", "fontSize": 7.5, "fontWeight": "bold" },
              { "type": "text", "value": "${payment.iban}", "fontSize": 8.5 },
              { "type": "text", "value": "${payment.bic}", "fontSize": 8.5 }
            ],
            "width": 156, "alignment": "left", "valign": "top",
            "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" }
          },
          {
            "content": [
              { "type": "text", "value": "BILLED TO", "fontSize": 7.5, "fontWeight": "bold" },
              { "type": "text", "value": "${customer.name}", "fontSize": 8.5 },
              { "type": "text", "value": "${customer.city}", "fontSize": 8.5 }
            ],
            "width": 156, "alignment": "left", "valign": "top",
            "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" }
          },
          {
            "content": [
              { "type": "text", "value": "ISSUED", "fontSize": 7.5, "fontWeight": "bold" },
              { "type": "text", "value": "${invoice.issued}", "fontSize": 8.5 },
              { "type": "text", "value": "VAT ${seller.vat_number}", "fontSize": 8.5 }
            ],
            "width": 155, "alignment": "left", "valign": "top",
            "borderSides": { "top": "false", "bottom": "false", "left": "false", "right": "false" }
          }
        ]
      ],
      "options": { "padding_x": 0, "padding_y": 2 }
    },

    { "type": "move", "y": 12 },
    { "type": "paragraph", "value": "${invoice.terms}", "options": { "fontSize": 8, "color": "8A8A8A" } }
  ]
}
invoice_minimal.data.json json
{
  "data": {
    "seller": {
      "name": "Inês Ferreira",
      "vat_number": "PT249883107"
    },
    "customer": {
      "name": "Vela Editorial Lda",
      "city": "Lisbon, Portugal"
    },
    "invoice": {
      "number": "2025-14",
      "issued": "28 Nov 2025",
      "due": "28 December 2025",
      "vat_rate": "23%",
      "terms": "Payment by bank transfer within 30 days. Reference 2025-14."
    },
    "items": [
      { "name": "Brand identity system", "amount": "2,400.00" },
      { "name": "Art direction retainer, Q4", "amount": "1,600.00" }
    ],
    "totals": {
      "net": "4,000.00",
      "vat": "920.00",
      "due": "€4,920.00"
    },
    "payment": {
      "iban": "PT50 0002 0123 1234 5678 9015 4",
      "bic": "CGDIPTPL"
    }
  }
}