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.
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.
| book | pages | to PDF | peak memory |
|---|---|---|---|
| Pride and Prejudice | 307 | 287 ms | 12 MiB |
| The Count of Monte Cristo | 1185 | 1.10 s | 47 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, ®istry);
let output = fleuron::layout::layout_book(&book, &styles, ®istry);
let bytes = fleuron::pdf::write(&output, ®istry, &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 pagesimport { 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.