Getting started

# Getting started

Install the gem, write a document class, call to_pdf. No fonts, binaries or services to set up.

## Install

One gem, standard library only.

```ruby
gem "stationery"
```

Stationery needs **Ruby 3.4 or newer** and has no runtime dependencies: no Prawn, no headless Chrome, no native extensions and no other processes. In a Rails app require the Railtie instead — see [Rails](https://stationery.zoolutions.llc/docs/rails):

```ruby
gem "stationery", require: "stationery/rails"
```

## Your first document

A class, a view_template, to_pdf.

A document is a `Stationery::Document` subclass. `view_template` describes the page with [elements](https://stationery.zoolutions.llc/docs/elements); `to_pdf` lays them out, paginates and returns the PDF bytes.

```ruby
require "stationery"

class Hello < Stationery::Document
  def view_template = text("Hello, world", size: 24, weight: :bold)
end

File.binwrite("hello.pdf", Hello.new.to_pdf)
```

No fonts to configure: **Inter** (regular, bold, italic, bold italic) ships inside the gem and is used until you declare a `font_family`. See [Fonts](https://stationery.zoolutions.llc/docs/fonts) for your own families.

## to_pdf

Bytes, a file, or any IO.

```ruby
pdf = Hello.new.to_pdf              # => "%PDF-1.7…" (binary String)
Hello.new.to_pdf("hello.pdf")       # also writes a path…
Hello.new.to_pdf($stdout)           # …or anything that responds to #write
Hello.new.to_pdf(strict: true)      # raise instead of writing when layout warned
Hello.new.to_pdf(debug: true)       # outline every layout rectangle
```

After a render, `document.warnings` lists everything the engine noticed but did not raise on — see [Warnings and strict mode](https://stationery.zoolutions.llc/docs/warnings).

## A real document

Page setup, fonts, a page template, a table.

```ruby
class InvoicePdf < Stationery::Document
  page size: :a4, margin: [40, 44, 56, 44]
  font_family "Inter", regular: "fonts/Inter-Regular.ttf", bold: "fonts/Inter-Bold.ttf"
  default_text font: "Inter", size: 9, color: "#1F2937"
  metadata title: "Invoice"

  page_template do |page|
    box(at: [44, page.height - 34], width: page.content_box.width) do
      text "Page #{page.number} of #{page.count}", size: 7, align: :right
    end
  end

  def initialize(invoice)
    super()
    @invoice = invoice
  end

  def view_template
    row(align: :middle) do
      column(width: 0.5) { text "Invoice #{@invoice.number}", size: 22, weight: :bold }
      column(width: 0.5, align: :right) { image "logo.png", height: 34, align: :right }
    end
    rule height: 3, color: "#4F46E5"
    spacer 18
    box(background: "#F3F4F6", radius: 6, padding: 12, width: 190) do
      text "AMOUNT DUE", size: 8, weight: :bold, letter_spacing: 0.5
      text @invoice.total, size: 19, weight: :bold, color: "#4F46E5"
    end
    table(@invoice.rows, width: :full, widths: [nil, 45, 80, 85], header: true, cell: { borders: [] }) do |t|
      t.row(0).set(background: "#4F46E5", color: "#FFFFFF", weight: :bold)
      t.columns(1..).align = :right
      t.zebra(from: 2, color: "#F9FAFB")
    end
  end
end
```

No cursor arithmetic anywhere: padding, backgrounds, borders, radii, column widths and page breaks are the engine's job. The [Cookbook](https://stationery.zoolutions.llc/docs/cookbook) walks through four complete documents.

## Render from the command line

stationery render FILE.

The gem ships a `stationery` executable. `render` loads a Ruby file, finds the `Stationery::Document` it defines and writes the PDF next to it:

```shell
stationery render hello.rb                  # writes hello.pdf
stationery render invoice.rb --out - > a.pdf
```

A document whose `initialize` needs arguments renders from a `def self.preview` class method that returns an instance built with sample data. Every option is on the [CLI](https://stationery.zoolutions.llc/docs/cli) page.

## The docs for an agent

Over MCP, as llms.txt, and every page as Markdown.

A coding agent reads these pages as tools over the [Model Context Protocol](https://modelcontextprotocol.io): `search_docs` finds sections, `get_page` reads a page as Markdown and `list_pages` names them all. In Claude Code:

```shell
claude mcp add --transport http stationery https://stationery.zoolutions.llc/mcp
```

Any other MCP client (Claude.ai, Cursor, Codex) takes the same URL as a remote server over HTTP:

```json
{ "mcpServers": { "stationery": { "url": "https://stationery.zoolutions.llc/mcp" } } }
```

It is read-only and needs no key. Without MCP, [/llms.txt](https://stationery.zoolutions.llc/llms.txt) is the index of the pages and [/llms-full.txt](https://stationery.zoolutions.llc/llms-full.txt) all of them in one file, and each page is Markdown at its URL with `.md`, as `/docs/elements.md`. The [Examples](https://stationery.zoolutions.llc/docs/examples) page carries the source of every example, so an agent gets them there too.

#### A skill that ships with the gem

The gem carries a skill for coding agents: how a document is built, the rules that surprise (a String is literal, a Float up to 1 is a share of the width, a fixed `height:` never splits), how to render a document to pictures and look at them before calling it done, conformance, labels and printing, and a recipe per thing people ask for (an invoice, a report, a form, a flyer, a label, a receipt), each pointing at the example that shows it. It is written from the README and the examples of the version installed, and carries that version:

```shell
stationery skill install                  # ~/.claude/skills/stationery (and Codex's, ~/.agents' when there)
stationery skill install --project        # ./.claude/skills/stationery, to commit with the app
stationery skill status                   # current, outdated or missing
stationery skill print > stationery.md    # one file, for an agent that takes one
```

Install it again after updating the gem; `status` says when it is behind. Every option is on the [CLI](https://stationery.zoolutions.llc/docs/cli) page.

## Where next

- [Examples](https://stationery.zoolutions.llc/docs/examples) — twenty complete documents with previews and live PDFs.
- [Documents and components](https://stationery.zoolutions.llc/docs/documents-and-components) — class-level configuration and reusable pieces.
- [Elements](https://stationery.zoolutions.llc/docs/elements) — every element and its options.
- [Forms](https://stationery.zoolutions.llc/docs/forms) — fillable fields, their options and how they behave in a viewer.
- [Layout rules](https://stationery.zoolutions.llc/docs/layout-rules) — how measuring, splitting and pagination decide where things land.
- [Comparison](https://stationery.zoolutions.llc/docs/comparison) — Stationery next to Prawn and sghtmltopdf, feature by feature.