auto-print / files / README.md
README.md
a6d0aed2 2026-10-24 Pavel Safronov | 1 | # auto-print |
| 2 | ||
| 3 | Turns markdown manuscripts into books: an epub, a docx, a pdf and a Kindle kpf for each one. | |
| 4 | ||
| 5 | It is made for writers who keep their drafts in markdown, next to their notes. | |
| 6 | A manuscript file can hold a spelling list, character notes and cut scenes. auto-print publishes only the story, | |
| 7 | plus the extra pages you name, such as a dedication or a character guide. | |
| 8 | ||
| 9 | - The source files are never changed. | |
| 10 | - One config file lists the books. One command builds all of them. | |
| 11 | - Under the hood, [pandoc](https://pandoc.org) does the conversion. A Chromium-based browser prints the pdf, and Amazon's Kindle Previewer makes the kpf. | |
| 12 | ||
a861fcd1 2026-10-24 Pavel Safronov | 13 | ## Why it exists |
| 14 | ||
| 15 | auto-print was developed to create books out of Pavel Safronov's upcoming writing. | |
| 16 | ||
| 17 | - Upcoming writing: [blog.fuzzyworld.net/upcoming](https://blog.fuzzyworld.net/upcoming/) | |
| 18 | - All writing: [blog.fuzzyworld.net](https://blog.fuzzyworld.net) | |
| 19 | ||
a6d0aed2 2026-10-24 Pavel Safronov | 20 | ## Requirements |
| 21 | ||
| 22 | - **Node 22.18 or newer.** | |
| 23 | - **pandoc 3.** It converts the markdown to epub, docx and html. Tested with pandoc 3.6.1. macOS: `brew install pandoc`. | |
| 24 | - **Google Chrome**, or Chromium, Edge or Brave, for the pdf and for generated covers. | |
| 25 | Set the `CHROME_PATH` environment variable to use a browser in a non-standard location. | |
| 26 | - **Kindle Previewer 4**, only for the kpf. macOS: `brew install --cask kindle-previewer`. | |
| 27 | Set the `KINDLE_PREVIEWER_PATH` environment variable to use a copy in a non-standard location. | |
| 28 | ||
| 29 | If a tool is missing, only the formats that need it fail. The other files are still written. | |
| 30 | ||
| 31 | ## Install | |
| 32 | ||
| 33 | Install the `auto-print` command from GitLab: | |
| 34 | ||
| 35 | ```sh | |
| 36 | npm install --global git+https://gitlab.com/PavelSafronov/auto-print.git | |
| 37 | ``` | |
| 38 | ||
| 39 | Or run it from a clone, with nothing installed. Node runs the TypeScript source directly: | |
| 40 | ||
| 41 | ```sh | |
| 42 | git clone https://gitlab.com/PavelSafronov/auto-print.git | |
| 43 | node auto-print/src/cli.ts --config path/to/books.json | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Usage | |
| 47 | ||
| 48 | ```sh | |
| 49 | auto-print # build every book in ./books.json | |
| 50 | auto-print my-novel # build only the named book(s) | |
| 51 | auto-print --config books/books.json | |
| 52 | auto-print --list # list the books, build nothing | |
| 53 | ``` | |
| 54 | ||
| 55 | The epub, docx and pdf take a few seconds in total. The kpf takes about 20 seconds per book. | |
| 56 | Each run overwrites the previous output files. | |
| 57 | ||
| 58 | ## The config file | |
| 59 | ||
| 60 | ```json | |
| 61 | { | |
| 62 | "outDir": "out", | |
| 63 | "defaults": { | |
| 64 | "author": "Your Name", | |
| 65 | "formats": ["epub", "docx", "pdf", "kpf"] | |
| 66 | }, | |
| 67 | "books": [ | |
| 68 | { | |
| 69 | "slug": "my-novel", | |
| 70 | "title": "My Novel", | |
| 71 | "source": "drafts/my-novel.md", | |
| 72 | "cover": "covers/my-novel.jpg" | |
| 73 | } | |
| 74 | ] | |
| 75 | } | |
| 76 | ``` | |
| 77 | ||
| 78 | Every path in the config file is relative to the folder of the config file. | |
| 79 | The files of each book are written to `<outDir>/<slug>/`, for example `out/my-novel/my-novel.epub`. | |
| 80 | ||
| 81 | | Top-level field | Required | Meaning | | |
| 82 | | --------------- | -------- | -------------------------------------------------------------------- | | |
| 83 | | `books` | yes | The list of books. | | |
| 84 | | `defaults` | no | Settings for every book. A book's own setting wins over the default. | | |
| 85 | | `outDir` | no | Output folder. Default `"auto-print-out"`. | | |
| 86 | ||
| 87 | | Book field | Required | Meaning | | |
| 88 | | --------------- | -------- | ----------------------------------------------------------------------------------------- | | |
| 89 | | `slug` | yes | Name of the output folder and files. Use lowercase words joined by dashes. | | |
| 90 | | `title` | yes | Book title, used on the cover, the title page and in the file metadata. | | |
| 91 | | `source` | yes | The markdown file. Its story section can link to other files, see "Collections of stories". | | |
| 92 | | `author` | yes | In the book or in `defaults`. | | |
| 93 | | `cover` | no | Cover image for the epub and kpf. Without it, a text cover is generated: title, "by", author. | | |
| 94 | | `section` | no | Heading that holds the story. Default `"Writing"`. Use `null` to take the whole file. | | |
| 95 | | `language` | no | Default `"en-US"`. | | |
| 96 | | `formats` | no | Default `["epub", "docx", "pdf", "kpf"]`. List fewer to skip some. | | |
| 97 | | `chapterBreaks` | no | epub and kpf only. Default `false`. See "Chapter breaks in the epub". | | |
| 98 | | `front` | no | Headings whose sections become pages before the story. See "Extra pages". | | |
| 99 | | `back` | no | Headings whose sections become pages after the story. See "Extra pages". | | |
| 100 | ||
| 101 | ## What happens to a manuscript | |
| 102 | ||
| 103 | 1. auto-print finds the heading named `Writing` (or the `section` you set). Any heading level, any letter case. | |
| 104 | If the name is used at several levels, the shallowest heading wins. | |
| 105 | 2. It takes everything under that heading, up to the next heading of the same or a higher level. | |
| 106 | Everything else in the file, such as notes or removed scenes, is left out. | |
| 107 | 3. It removes HTML comments, such as a blog's `<!--more-->` marker. | |
| 108 | 4. It promotes the headings, so the story's top headings become chapters. If the story's chapters are `###` headings, they become level 1. | |
| 109 | 5. Text before the first chapter heading, such as a content warning, is kept as an opening section. | |
| 110 | 6. Images are found relative to the file they are written in, so `` works. | |
| 111 | ||
| 112 | A manuscript might look like this: | |
| 113 | ||
| 114 | ```markdown | |
| 115 | # My Novel | |
| 116 | ||
| 117 | ## Notes | |
| 118 | ||
| 119 | Things only the author should see. | |
| 120 | ||
| 121 | ## Writing | |
| 122 | ||
| 123 | [Content warning: peril.] | |
| 124 | ||
| 125 | ### Chapter One | |
| 126 | ||
| 127 | #### The morning | |
| 128 | ||
| 129 | The hero woke up. | |
| 130 | ||
| 131 | ## Removed writing | |
| 132 | ||
| 133 | A scene that was cut. | |
| 134 | ``` | |
| 135 | ||
| 136 | ## Collections of stories | |
| 137 | ||
| 138 | A book can be a collection of stories that each live in their own file. | |
| 139 | The book's `source` is then an index file, and its `Writing` section holds only links to the story files, one per line: | |
| 140 | ||
| 141 | ```markdown | |
| 142 | ## Writing | |
| 143 | ||
| 144 | [The first story](./story-1.md) | |
| 145 | ||
| 146 | [The second story](./story-2.md) | |
| 147 | ||
| 148 | <!-- [The third story](./story-3.md) --> | |
| 149 | ``` | |
| 150 | ||
| 151 | - auto-print reads the `Writing` section of each linked file, in the order of the links. | |
| 152 | - A link inside an HTML comment (`<!-- -->`) is left out. Use this for a story that is not ready. | |
| 153 | - A `Writing` section that mixes links with other text stops the build with an error. | |
| 154 | ||
| 155 | Each story file is handled with the rules in "What happens to a manuscript", with one difference: | |
| 156 | the text before a story's first heading, such as its content warning, is moved to just below that heading. | |
| 157 | Without this move, a story's content warning would appear at the end of the previous story. | |
| 158 | ||
| 159 | ## Extra pages | |
| 160 | ||
| 161 | Pages such as a dedication, a content warnings page, a character guide or a map are sections in the book's source file. | |
| 162 | The `front` and `back` lists name the headings of those sections, in the order they appear in the book. | |
| 163 | ||
| 164 | ```json | |
| 165 | { | |
| 166 | "slug": "my-collection", | |
| 167 | "title": "My Collection", | |
| 168 | "source": "my-collection.md", | |
| 169 | "front": ["Dedication", "Content Warnings"], | |
| 170 | "back": ["Characters", "Map"] | |
| 171 | } | |
| 172 | ``` | |
| 173 | ||
| 174 | ```markdown | |
| 175 | ## Dedication | |
| 176 | ||
| 177 | To my city. | |
| 178 | ||
| 179 | ## Characters | |
| 180 | ||
| 181 | ### The hero | |
| 182 | ||
| 183 | The one the stories are about. | |
| 184 | ||
| 185 | ## Map | |
| 186 | ||
| 187 |  | |
| 188 | ``` | |
| 189 | ||
| 190 | Only the listed sections are published. Other sections, such as notes, stay private. | |
| 191 | ||
| 192 | - Headings match ignoring letter case. If a name is used at several heading levels, the shallowest heading wins. | |
| 193 | So a `## Characters` page is published, and `### Characters` notes inside a `## Notes` section are not. | |
| 194 | - Two headings with the same name at the same level stop the build with an error. So does a listed name that is not in the file. | |
| 195 | - The section heading becomes the page title. Headings inside the section become sub-headings of the page. | |
| 196 | - Every extra page starts on a new page, in every format. | |
| 197 | - A section named `Dedication` has the typical look of a dedication: centered, in italics, about a third of the way down the page. | |
| 198 | Its heading is not shown, and it is left out of the table of contents. | |
| 199 | - A section named `Copyright` is left out of the table of contents. | |
| 200 | - In the epub and the kpf, front pages are marked as front matter, and back pages as back matter (appendices). | |
| 201 | E-readers use these marks to know where the story starts. | |
| 202 | ||
| 203 | ## Chapter breaks in the epub | |
| 204 | ||
| 205 | The `chapterBreaks` setting controls how the chapters appear in the epub and the kpf. | |
| 206 | ||
| 207 | - `false` (the default): the story flows continuously. A chapter is only a heading in the text. | |
| 208 | - `true`: every chapter starts on a new page. | |
| 209 | ||
| 210 | In both cases the chapters are listed in the table of contents. | |
| 211 | The docx and the pdf always flow continuously. | |
| 212 | ||
| 213 | ## Sentences on separate lines | |
| 214 | ||
| 215 | A manuscript can put each sentence on its own line, or write each paragraph as one long line. | |
| 216 | Both styles produce the same book. | |
| 217 | ||
| 218 | - A single line break joins the two lines with a space, inside the same paragraph. | |
| 219 | - A new paragraph starts only at an empty line. A line that holds only spaces also counts as empty. | |
| 220 | ||
| 221 | This works because auto-print reads the files as GitHub-flavored markdown (`gfm`), where a single line break is not a real line break. | |
| 222 | ||
| 223 | A line can be read as markdown formatting by mistake. Avoid these at the start or end of a line: | |
| 224 | ||
| 225 | | In the manuscript | What happens | | |
| 226 | | ---------------------------------------------------------- | ----------------------------------------------- | | |
| 227 | | A line that starts with `- `, `* `, `+ ` or `1. ` | It starts a list and ends the paragraph. Other numbers, such as `2. `, are safe. | | |
| 228 | | A line of only `---` or `===`, directly after a text line | The line above it becomes a heading. | | |
| 229 | | A line that starts with `#` and a space | It becomes a heading. | | |
| 230 | | A line that starts with `>` | It becomes a block quote. | | |
| 231 | | A line that starts with `:::` | It starts or ends a styled block. | | |
| 232 | | Two spaces, or a `\`, at the end of a line | A real line break is kept inside the paragraph. | | |
| 233 | | `{.word}`, `{#word}` or `{key=value}` at the end of a heading, or right after an image or link | It is read as a setting and not shown. Other braces, like `{quietly}`, are shown as written. | | |
| 234 | ||
| 235 | A dash such as `—` at the start of a line is safe. | |
| 236 | ||
| 237 | ## How each format is made | |
| 238 | ||
| 239 | - **epub**: pandoc, with a table of contents built from the headings. | |
| 240 | The stylesheet is pandoc's default one, plus [assets/epub.css](assets/epub.css). | |
| 241 | - **docx**: pandoc, with [assets/reference.docx](assets/reference.docx) as the Word template. | |
| 242 | It is pandoc's default template plus a `Dedication` paragraph style. Chapters use the `Heading 1` style. | |
| 243 | - **pdf**: pandoc makes a standalone html page with [assets/print.css](assets/print.css), then a headless browser prints it. | |
| 244 | The browser does not always exit by itself after printing, so auto-print waits for the pdf and then stops the browser. | |
| 245 | - **kpf**: Kindle Previewer's command line tool converts an epub into a kpf. | |
| 246 | auto-print builds a fresh epub for this, so the kpf works even if `epub` is not in `formats`. | |
| 247 | ||
| 248 | ## Development | |
| 249 | ||
| 250 | ```sh | |
| 251 | npm install # TypeScript and Node's type definitions, for the checks below | |
| 252 | npm test # unit tests, plus pandoc tests that are skipped when pandoc is missing | |
| 253 | npm run typecheck | |
| 254 | npm run build # compiles src/ to dist/, which the installed command runs | |
| 255 | ``` | |
| 256 | ||
| 257 | The code has no runtime dependencies. [decisions.md](decisions.md) records why it works the way it does. | |
| 258 | ||
| 259 | `assets/reference.docx` is made by [scripts/make-reference-docx.sh](scripts/make-reference-docx.sh). | |
| 260 | Run it again to change the Word styles. | |
| 261 | ||
| 262 | ## License | |
| 263 | ||
| 264 | [MIT](LICENSE) |