soli-pdf

Business letter

A letter with inline rich text: bold, italic, monospace and clickable links on the same line.

Paragraphs use spans to mix weights and colours within one wrapped flow, and a link span becomes a real link annotation in the PDF.

Rich text spans Links 1 page
Page 1
Page 1 of 1
render_letter.rb ruby
require 'json'
require 'soli/pdf'

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

pdf = Soli::PDF.render(
  template: template,
  data: data,
  title: 'Letter to Maison Lumière'
)
File.binwrite('letter.pdf', pdf)

Run it yourself

  1. Add the gem to your Gemfile — see Installation.
  2. Download letter.template.json and letter.data.json.
  3. Run ruby render_letter.rb.
letter.template.json json
{
  "fonts": ["titillium"],
  "footer": [
    { "type": "paragraph", "value": "${company.name} — ${company.email}", "options": { "alignment": "center", "fontSize": 8 } }
  ],
  "content": [
    { "type": "paragraph", "value": "${company.name}", "options": { "fontSize": 22, "fontWeight": "bold" } },
    { "type": "paragraph", "value": "${company.tagline}", "options": { "fontSize": 11 } },
    { "type": "move", "y": 8 },
    { "type": "paragraph", "value": "${letter.place_date}", "options": { "alignment": "right", "fontSize": 10 } },
    { "type": "move", "y": 18 },
    { "type": "paragraph", "value": "${letter.greeting}", "options": { "fontSize": 11 } },
    { "type": "move", "y": 10 },
    { "type": "paragraph", "value": "${letter.body1}", "options": { "fontSize": 11 } },
    { "type": "move", "y": 8 },
    { "type": "paragraph", "value": "${letter.body2}", "options": { "fontSize": 11 } },
    { "type": "move", "y": 10 },
    { "type": "paragraph", "options": { "fontSize": 11 }, "spans": [
      { "text": "P.S. Inline rich text — run " },
      { "text": "soli serve .", "mono": true },
      { "text": " to preview, read the " },
      { "text": "style guide", "italic": true },
      { "text": " for the " },
      { "text": "details", "fontWeight": "bold" },
      { "text": ", or visit " },
      { "text": "our docs", "link": "https://soli.example/docs", "color": "1D4ED8" },
      { "text": "." }
    ] },
    { "type": "move", "y": 16 },
    { "type": "paragraph", "value": "${letter.closing}", "options": { "fontSize": 11 } },
    { "type": "move", "y": 6 },
    { "type": "paragraph", "value": "${letter.signature}", "options": { "fontSize": 11, "fontWeight": "bold" } }
  ]
}
letter.data.json json
{
  "data": {
    "company": {
      "name": "Atelier Nord",
      "tagline": "Design & fabrication studio",
      "email": "studio@ateliernord.fr"
    },
    "letter": {
      "place_date": "Paris · 28 November 2025",
      "greeting": "Dear Maison Lumière,",
      "body1": "Thank you for trusting us with the rebranding of your flagship boutique. We have completed the identity system and the six signage panels, and scheduled installation for the week of 15 December as agreed.",
      "body2": "The final invoice (AN-2025-0042) is attached separately. Please don't hesitate to reach out if anything needs adjusting before installation day — we'd rather get it right than get it fast.",
      "closing": "Warm regards,",
      "signature": "Camille Mercier — Atelier Nord"
    }
  }
}