Reference

Comparison

Stationery next to Prawn and sghtmltopdf: what each one does, what only one does, and when to pick which.

The three#

Different shapes of the same job.

Stationery 0.11Prawn 2.5 + prawn-table 0.2sghtmltopdf 0.5
What it isA layout engine with a Phlex-style component DSLA cursor API over a PDF writerAn HTML and CSS renderer written in Rust
InputRuby components; html and markdown elements for user contentRuby calls: text, bounding_box, tableAn HTML document with CSS
RuntimePure Ruby, no runtime dependenciesPure Ruby (pdf-core, ttfunk, matrix)Native extension, precompiled for Linux and macOS; Windows delegates to a server process
LicenceMITRuby / GPL-2 / GPL-3MIT
First releaseSeptember 20262008July 2026

Every claim below was checked against the code of these versions. "Partial" means the feature exists with a documented limit; the Limitations page has Stationery's.

Layout and pages#

StationeryPrawnsghtmltopdf
Layout modelRows, columns, boxes, stack/layer, wrap, floats; the engine measures and placesYou compute positions; bounding_box, move_down, cursorCSS block, inline, flexbox, grid, floats, absolute positioning
Page breakspage_break, break_inside: :avoid/:auto, keep_with_next, keep_together, min_heightstart_new_page; group raises NotImplementedErrorbreak-before/after/inside, page-break-*
Orphans and widowsorphans: and widows: on text, default 1NoYes, default 2
Headers, footers, page n of NPage templates with any content, header/footer, regionsrepeat(:all) blocks with number_pages@page margin boxes and header/footer HTML with placeholders
Tables across pagesSplit rows, repeated header, colspan/rowspan, split_rows:prawn-table: header repeat, spans, splittingYes, thead repeats
Balanced columnscolumns(count:, gap:, balance:, rule:), across pagesNoNo
Text wrap around images and boxesfloat: :left/:right on image and box, and on img in htmlNoFloats
Page labelsYesNoNo

Text and fonts#

StationeryPrawnsghtmltopdf
KerningGPOS pairs and kern table, on by defaultkern table, on by defaultHarfBuzz-class shaping (harfrust)
Ligatures and OpenType featuresliga by default; smcp, onum, tnum, ss01… on request; no contextual lookupsNoFull shaping through harfrust
HyphenationSoft hyphens; patterns for English, German and SwedishSoft hyphens onlySoft hyphens only (hyphens: auto has no dictionary)
JustificationYes, widens spacesYesYes
Fallback fontsPer character, then bundled Interfallback_fonts per boxFont stack per element
CJK line breakingBetween ideographs, kana and Hangul with kinsoku for closing and opening marks; zero-width space and <wbr>Partial: zero-width spacesYes, UAX #14, word-break
Right-to-left, Arabic, IndicNot built in: drawn glyph by glyph, left to right. A shaper hook draws what the application's HarfBuzz places, within a stretch of one font and style; no reordering across stretches or linesdirection: :rtl reverses runs; no shapingShaping yes; direction: rtl and vertical writing no
Colour emojiNo, draws .notdefNoCBDT, sbix, COLR v0
Font formatsTTF, OTF/CFF, TTC, WOFFTTF, OTF/CFF, TTC, DFontTTF, OTF
WOFF2, variable fontsNoNoNo
SubsettingYes, TrueType and CFFYesYes
Bundled fontsInter; font packs by CLINone (standard 14 unembedded)None; system fonts optional

Images and drawing#

StationeryPrawnsghtmltopdf
JPEG, PNGYesYesYes
WebP, GIFLossless WebP; lossy WebP and GIF noNoWebP yes, GIF no
SVGInline and file: shapes, paths, gradients, text, stylesheets, use, symbol, clipPath; no mask, pattern, filterNo (prawn-svg is a separate gem)By reference through <img> (usvg); inline <svg> no
Oversized-image warning, downscalingYes; PNG and WebP resamplingNoNo
Rotation, shadows, overlaysrotate: on images and boxes, shadow:, stack/layerrotate blocks; transparent; no shadowsCSS transform, box-shadow, positioning
Direct drawingcanvas with paths, clips, transformsFull graphics APINo

PDF features#

StationeryPrawnsghtmltopdf
Links, outline, table of contentsYes, yes, table_of_contents elementLinks yes, outline yes, no TOC elementLinks yes; outline no; --toc
Forms (AcroForm)Text, checkbox, radio, select, signature fields; appearances in the document's fontsNoNo (<input> is drawn, not interactive)
Tagged PDF, PDF/UA-1Yes, veraPDF-verifiedNoNo
PDF/A2b and 3b, veraPDF-verifiedNoNo
XMP metadataYesNoNo
Embedded files, Factur-X / ZUGFeRDYes; profiles MINIMUM to XRECHNUNG, Mustang-validatedNoNo
EncryptionAES-256, AES-128, RC4-128RC4 40 and 128 bitNo
Digital signaturesCAdES detached, PAdES baseline B-B and B-T (RFC 3161 timestamp), RSA or ECDSA; no LTV, one signatureNoNo
Page labelsYesNoNo

Working with it#

StationeryPrawnsghtmltopdf
HTML or Markdown inputhtml and markdown elements: structure and inline marksNo (third-party gems)Its whole input
CSSPartial: a property subset on the html element (colours, font size, weight and style, decoration, alignment, backgrounds, padding, margins, table borders and widths, page breaks) with element, class and id selectors. float on img only. No display, flexbox, grid, positioning, font-family or selectors with combinatorsNoA cascade with custom properties, calc(), :has(); flexbox, grid, floats, positioning
Untrusted contentLink scheme allow-list, no remote fetches, image base_path: sandbox, bounded nestingNot applicableNode, depth and image size caps, file sandbox
Testing helpersInspector, RSpec matchers, Minitest assertionspdf-inspector (separate gem)None
InstrumentationActiveSupport::Notifications eventsNoNo
Warnings instead of silent dropsdocument.warnings, strictNoWarnings on stderr
Railsrender pdf:, send_pdf, previewsCommunity gemsrender pdf: in the wicked_pdf style
Streaming outputto_pdf { |chunk| } streams the write phase; incremental: true writes each page as it is painted, so memory follows what is left to paint (the document is built in full first)render_file onlyYes: page-by-page streaming to a block or a sink, bounded memory
Performance regression gaterake metrics in CINocargo bench metrics in CI

Speed#

Same documents, same fonts; see Performance for the method.

sghtmltopdf is native code and is faster on text and tables (2 to 3.5 times); on a page of photographs the two are level. Stationery renders faster than Prawn (1.6 to 6 times) and writes smaller files than both. The numbers, the machine and the way they were taken are on the Performance page, and bundle exec rake bench runs all three engines on yours.

When to pick which#

Pick Stationery when the PDF is the product: invoices, contracts, reports, flyers and forms that need bookmarks, accessibility, PDF/A, e-invoicing, encryption or a signature, built from the same Ruby components as the rest of the app, with tests that read the PDF back. It stays pure Ruby, so there is nothing to compile, no browser and no server process.

Pick sghtmltopdf when you already have HTML templates and CSS, need CSS layout (flexbox, grid, floats), complex-script text or colour emoji, or must render very large documents with bounded memory, and can live without outlines, forms, tagged PDF, PDF/A and encryption. It is faster on text and tables, at the price of a native extension.

Pick Prawn when you have a Prawn codebase, want the lowest-level control over every coordinate, or need its long track record. New documents that want layout done for them are less work in either of the other two.