CSS paged media, in Rust

A typesetting engine
for novels.

fleuron turns a manuscript into a typeset book and renders it as a print ready PDF. Styling is CSS. It is built for markdown writing tools. Test it out in the playground below.

CHAPTER IThe Plan of the Parliament of Erln their ruddy jackets of leather that reached to their knees theImen of Erl appeared before their lord, the stately white-hairedman in his long red room. He leaned in his carven chair andheard their spokesman.And thus their spokesman said."For seven hundred years the chiefs of your race have ruled uswell; and their deeds are remembered by the minor minstrels, livingon yet in their little tinkling songs. And yet the generations streamaway, and there is no new thing.""What would you?" said the lord."We would be ruled by a magic lord," they said.
page 1

Fleuron playground. Change the manuscript or the stylesheet and watch the book update in real time.

Paged media

A built-in stylesheet sets the defaults and yours overrides whatever you want to change. fleuron supports a subset of CSS, and reports what it does not. Use CSS to configure all the furnishings of a professionally typeset novel: trim size, drop caps, page numbers, running heads, and much more.

@page {
  size: 5.5in 8.5in;
  margin: 0.8in 0.7in;

  @bottom-center {
    content: counter(page);
  }
}

@page :left {
  @top-left {
    content: "Pride and Prejudice";
  }
}

section {
  break-before: recto;
}

p {
  orphans: 2;
  widows: 2;
}

Performance

fleron is built for speed. Two complete novels, measured from markdown to PDF.

Layout time and peak memory for two public-domain novels.
bookpagesto PDFpeak memory
Pride and Prejudice307287 ms12 MiB
The Count of Monte Cristo11851.10 s47 MiB

Apple M-series. Run it on yours.

Using it

fleuron is the typesetting engine, pure Rust with no I/O.fleuron-markdown reads a manuscript into the content tree. fleuron-cli takes a manuscript and stylesheets and writes a PDF. fleuron-wasm runs the same pipeline in a worker.

let markdown = std::fs::read_to_string("manuscript.md")?;
let (sections, warnings) =
    to_sections(&markdown, "manuscript.md", &Options::default());
let book = assemble(frontmatter(&markdown), sections);

let css = std::fs::read_to_string("book.css")?;
let mut registry = bundled_registry()?;
let mut sheets = Stylesheets::parse(&[Source::author("book.css", &css)]);
sheets.load_fonts(&mut registry, &Files(PathBuf::from(".")));
let styles = sheets.compile(&book, &registry);

let output = fleuron::layout::layout_book(&book, &styles, &registry);
let bytes = fleuron::pdf::write(&output, &registry, &book.metadata)?;
std::fs::write("book.pdf", bytes)?;
cargo install fleuron-cli

fleuron manuscript.md -o book.pdf -c book.css
# fleuron: manuscript.md → book.pdf: 333 pages
import { Preview } from 'fleuron';

// Starts the worker, loads the module, keeps the session.
const preview = await Preview.mount(document.querySelector('#book'));
await preview.setStyle(css);
await preview.setMarkdown(markdown);

preview.page = 12;
const pdf = await preview.exportPdf();

fleuron is pre-alpha. It is being built as the pagination backend for Orca, an Obsidian plugin for writing novels.