Skip to content

Repository files navigation

Crown

Create books using HTML, CSS, and PrinceXML

Crown is a modern development framework that brings the best of web development to PDF book creation. Write your content in Markdown, style it with CSS, and generate print-quality PDFs with hot reload and instant preview.

Features

  • Markdown content with YAML frontmatter, ordered however you like
  • Handlebars templates with partials and custom helpers
  • Hot reload — save a file, watch the PDF update in the browser
  • PrinceXML for the typesetting web browsers can't do
  • Type-safe config with autocomplete via defineConfig
  • Data-driven pages from CSV, JSON or YAML
  • Imposition — arrange finished pages onto press sheets for folding and stapling

Quick Start

# Create a new book project
npx crown create my-book

# Start development
cd my-book
npm install
npm run dev

Your browser will open with a live preview of your PDF. Edit any file and watch it update instantly!

Installation

# As a project dependency
npm install @romello/crown

# Or use directly with npx
npx @romello/crown create my-book

Requirements:

  • Node.js 18 or higher
  • PrinceXML installed and in your PATH (download here)

PrinceXML is only needed to turn HTML into PDF. crown layout works on PDFs you already have, so it runs without Prince installed.

Project Structure

my-book/
├── src/
│   ├── content/          # Markdown content files
│   │   ├── 00-title.md
│   │   ├── 01-intro.md
│   │   └── 02-chapter-one.md
│   ├── templates/        # Handlebars templates
│   │   ├── layout.html
│   │   └── partials/
│   │       ├── title.html
│   │       └── chapter.html
│   └── styles.css        # Main stylesheet
├── crown.config.js       # Configuration
└── dist/                 # Generated files
    ├── book.html
    └── book.pdf

Configuration

Create a crown.config.js file in your project root:

import { defineConfig } from "@romello/crown";

export default defineConfig({
  input: {
    content: "src/content/**/*.md",
    template: "src/templates/layout.html",
    styles: "src/styles.css",
    assets: "src/fonts", // Optional: copied to output directory
  },
  output: {
    html: "dist/book.html",
    pdf: "dist/book.pdf",
  },
  metadata: {
    title: "My Book",
    author: "Your Name",
    subject: "A great book",
    keywords: ["book", "crown"],
  },
  page: {
    size: "5.5in 8.5in",
    margins: {
      top: "0.75in",
      bottom: "0.75in",
      inside: "0.75in",
      outside: "0.5in",
    },
  },
});

Writing Content

Content files are written in Markdown with YAML frontmatter:

---
title: Chapter One
subtitle: The Beginning
id: chapter-1
order: 1
---

# Chapter One

Your content here...

The order field determines the sequence of content in your book.

Templates

Crown uses Handlebars for templating. Access your content and metadata in templates:

<!DOCTYPE html>
<html lang="{{metadata.lang}}">
  <head>
    <title>{{metadata.title}}</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    {{#each content}}
    <section class="chapter" id="{{frontmatter.id}}">
      <h1>{{frontmatter.title}}</h1>
      {{{html}}}
    </section>
    {{/each}}
  </body>
</html>

Built-in Helpers

  • {{markdown text}} - Render inline markdown
  • {{json data}} - Pretty-print JSON
  • {{formatDate date}} - Format dates
  • {{eq a b}} - Equality check
  • {{gt a b}}, {{lt a b}} - Comparisons
  • {{length array}} - Array length
  • {{join array separator}} - Join array elements

CSS for Print

Crown automatically generates an @page rule from your page config (size and margins). If your CSS already includes an @page rule, Crown will use yours instead.

Style your books with CSS, including Prince-specific properties:

/* @page is auto-generated from config — only add this if you need custom overrides */
@page {
  size: 5.5in 8.5in;
  margin: 0.75in;
}

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

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

h1 {
  break-before: recto; /* Start chapters on right page */
  break-after: avoid; /* Keep with following content */
}

CLI Commands

# Scaffolding
crown create my-book                # Create a new project

# Development
crown dev                           # Start dev server
crown dev --port 3001               # Custom port
crown dev --no-open                 # Don't open browser

# Building
crown build                         # Build production PDF
crown build -c path/to/config.js    # Use a specific config file
crown build --output path/to.pdf    # Custom output path
crown build --verbose               # Show detailed output

# Imposition
crown layout book.pdf               # Impose onto press sheets
crown layout --list                 # Show formats and sheet sizes

# Utilities
crown watch                         # Watch and rebuild (no server)
crown preview:html                  # Open HTML in browser
crown preview:pdf                   # Open PDF in viewer
crown doctor                        # Check the environment

All commands accept -c/--config <path> to specify a config file. This is useful for monorepos or projects with multiple books.

Layout & Imposition

crown build gives you a PDF of finished pages, one after another. crown layout arranges those pages onto press sheets so that printing, folding and stapling produces a book that reads in order — the step usually called imposition.

crown layout book.pdf                                  # quarter-letter zine, the default
crown layout book.pdf -f sixth -o sheets.pdf           # six up on a letter sheet
crown layout books/*.pdf -o print/                     # a whole edition at once
crown layout cover.pdf -f half -i cover                # a 4-page wrap cover
crown layout eight.pdf -f magic -i magic               # one sheet, fold and cut

How it works. A sheet is tiled into a grid of cells, one finished page each. Two side-by-side cells make a folio — a piece of paper with a fold between them, four pages once it's printed on both sides. Folios are cut apart, nested inside one another, and stapled through the fold. Only the grid changes between formats.

--format grid page on letter pages/sheet
half 2 × 1 5.5 × 8.5 4
quarter-portrait (default) 2 × 2 4.25 × 5.5 8
quarter-landscape 2 × 2 5.5 × 4.25 8
sixth 2 × 3 4.25 × 3.67 12
eighth-landscape 2 × 4 4.25 × 2.75 16
eighth-portrait 4 × 2 2.75 × 4.25 16
magic 4 × 2 2.75 × 4.25 8, one side

Formats are named by the grid rather than the paper, so --sheet letter|legal|tabloid|a4|a3 gives the same layout at another size — -f quarter-portrait -s tabloid yields 5.5 × 8.5 pages.

--imposition picks what's being made: zine nests every folio into one stitched book (the default), cover imposes a 4-page wrap and repeats it to fill the sheet, and magic is the one-sheet eight-page zine you fold into eighths and cut through the middle.

Duplex matters. A fold runs vertically down the sheet, so the back must mirror left-to-right: that's the long edge on a portrait sheet and the short edge on a landscape one. The command prints which to use. If your printer only flips the other way, pass --duplex long|short and the backs are mirrored to compensate.

Other options: --signature <pages> splits a long book into several nested signatures instead of one thick one, and --marks draws trim and fold ticks at the sheet edges. Books that aren't a multiple of 4 are padded with blanks, and pages that don't exactly match the cell are scaled to fit rather than distorted.

Data-Driven Content

Load external data into your templates:

// crown.config.js
export default defineConfig({
  // ...
  data: {
    sales: "./data/sales.csv",
    authors: "./data/authors.json",
    stats: "./data/stats.yaml",
  },
});

Access in templates:

<table>
  {{#each data.sales}}
    <tr>
      <td>{{month}}</td>
      <td>{{amount}}</td>
    </tr>
  {{/each}}
</table>

Custom Helpers

Add custom Handlebars helpers:

// src/templates/helpers.js
export default {
  uppercase(str) {
    return str.toUpperCase();
  },
  formatPrice(amount) {
    return `$${amount.toFixed(2)}`;
  },
};

Reference in config:

export default defineConfig({
  // ...
  helpers: "src/templates/helpers.js",
});

Markdown Extensions

Crown supports custom marked extensions for features like footnotes, math, etc.:

export default defineConfig({
  // ...
  markdown: {
    gfm: true, // GitHub Flavored Markdown (default: true)
    breaks: false, // Convert \n to <br> (default: false)
    extensions: [
      "./src/extensions/footnotes.js", // Custom marked extensions
    ],
  },
});

Each extension file should export a valid MarkedExtension object.

Contributing

docs/development.md covers the build, the test suite and the patterns this codebase follows. In short: npm install, npm run dev for watch mode, npm test before you push.

Issues and pull requests welcome.

Links

License

MIT — by Romello Goodman.

About

A framework for creating books using html, css, and prince xml

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages