Skip to content

Latest commit

Β 

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

doc-engine-cli

Zero-config Markdown β†’ PDF documentation engine

License: MIT Python 3.10+ PyPI PyPI Downloads Typst Tests Sponsor

Transform any README.md into a premium, print-ready PDF report β€” no configuration, no templates, no LaTeX.


doc-engine-cli turning a Markdown file into a PDF
pipx install doc-engine-cli

Sponsor doc-engine-cli on GitHub Sponsors

Built and maintained by one person, in the open. Sponsorship keeps it that way.


Overview

doc-engine-cli is a developer-first CLI tool that converts Markdown files into professionally styled PDF documents using Typst as its rendering backend. It is designed for teams and individual developers who need high-quality documentation artifacts without the complexity of LaTeX or manual typesetting.

The tool auto-detects your README.md, extracts metadata from Git, and produces an IEEE-inspired technical document β€” complete with cover page, table of contents, and premium typography β€” in a single command.

doc-engine build

That's it. Zero configuration required.


Features

Feature Description
Zero-Config Auto-detects README.md, Git author, and document title. No setup files needed.
Seven Templates Academic, article, book, minimal, modern, report, and technical layouts, each with a configurable accent color. Point --template at your own .typ file to go further.
Front Matter An optional --- metadata block sets the title, subtitle, author, template, and accent right inside the file.
Watch Mode --watch rebuilds the PDF every time you save the source.
Diagrams ```mermaid and ```svg blocks are rendered as real diagrams, in pure Python β€” no Node, no headless browser.
Alerts > [!NOTE] and friends render as coloured callouts, the way GitHub shows them.
Math LaTeX math, inline with $…$ and display with $$…$$, translated into native Typst math.
Multi-File A doc-engine.md manifest builds one PDF from many files, diagrams, figures, and a bibliography.
Page Sizes A4 by default, plus A3–A6, ISO/JIS B5, and US letter, legal, and tabloid.
Project Config A .doc-engine.toml keeps a team's defaults out of every document.
Rich Markdown Embeds local images, renders GitHub task lists as real checkboxes, and turns [^1] footnotes into native Typst footnotes.
Error Checking Reports source problems with line and column before compiling. A --dry-run mode runs the check on its own.
Non-Destructive Never overwrites an existing PDF β€” writes report (1).pdf, report (2).pdf, … unless you pass --force.
Premium Typography Font stacks that end in a font Typst ships, so a document looks the same in a bare container as on a laptop.
Pure Python No external binaries required (no Pandoc, no LaTeX). Ships as a single pip install.
Cross-Platform Works on Windows, macOS, and Linux with Python 3.10+.

Academic Writing

Cite with the usual [@citation-key] syntax and drop a refs.bib, references.bib, or bibliography.bib next to your document. It is picked up automatically and rendered as an IEEE-styled references section:

As shown in [@smith2020], results vary.

Point --bib at a different file, or name one in front matter, to override the search. Combine it with the academic, article, or report layout and LaTeX math for a paper that needs no LaTeX toolchain.


Quick Start

Installation

pipx install doc-engine-cli

(If you don't have pipx, you can install it via pip install pipx)

Note

Installing with a pipx older than 1.17 could crash with InvalidVersion: Invalid version: ''. That came from a dependency and was fixed upstream in mermaidx 0.9.5, so a fresh install is fine. If you are holding an older mermaidx, upgrade it, or use uv:

uv tool install doc-engine-cli

Generate Your First PDF

Navigate to any project directory containing a README.md and run:

doc-engine build

The tool will:

  1. Auto-detect README.md in the current directory
  2. Extract the document title from the first # heading
  3. Read your Git user.name for the author field
  4. Generate a README_doc.pdf with cover page, ToC, and formatted content

Explicit Options

doc-engine build path/to/file.md -o output.pdf -t "Custom Title" -a "Author Name"

Usage

doc-engine-cli configuration demo
Switching templates, recoloring the accent, and checking a file for errors.

Commands

doc-engine build [INPUT_FILE]   Convert a Markdown file into a PDF
doc-engine info                 Show version, repository, and what is supported
doc-engine info --json          The same, as JSON, for tools that drive the CLI
doc-engine --version            Print the version and exit
doc-engine --help               Show all commands and flags

build flags

Flag Default Description
INPUT_FILE auto-detect README.md Path to the Markdown file to convert.
-o, --output <input>_doc.pdf Output PDF path.
-t, --title first # heading Document title override.
-s, --subtitle none Subtitle shown under the title on the cover.
-a, --author git config user.name Author name override.
--date today Date shown on the cover.
--template academic A built-in layout (academic, article, book, minimal, modern, report, technical) or a path to your own .typ file.
--accent template default Accent color as a hex value (#2563eb) or a name (blue, teal, rose, ...).
--paper a4 Page size: a3–a6, iso-b5, jis-b5, us-letter, us-legal, us-tabloid.
--bib auto-detect refs.bib Path to a custom .bib file for the bibliography.
--pdf-standard off Write an archival PDF/A file: a-2b or a-3b.
--code-theme Typst default Syntax highlighting theme: github, solarized, monochrome, or a path to a .tmTheme.
--tall-images fit What to do with a picture taller than a page: fit scales it onto one page, split cuts it across several.
--fetch-images off Download images linked by URL instead of rendering their alt text.
--no-branding off Hide the doc-engine attribution from the PDF.
--dry-run off Check the Markdown for errors and exit without writing a PDF.
-w, --watch off Rebuild automatically whenever the source file changes.
-f, --force off Overwrite the output file instead of writing a numbered copy.
--open off Open the PDF after it is generated.

Any flag can also be set in the front matter (see below); a flag on the command line always wins.

Examples

Basic β€” zero-config mode:

cd my-project
doc-engine build
# β†’ Generates README_doc.pdf

Specify input and output:

doc-engine build CONTRIBUTING.md -o contributing_guide.pdf

Override metadata:

doc-engine build -t "API Reference v2.0" -a "Engineering Team"

Pick a template and accent color:

doc-engine build --template modern --accent teal
doc-engine build --template technical --accent "#7c3aed"

Check for errors before building:

doc-engine build --dry-run

Drop the engine attribution from the PDF:

doc-engine build --no-branding

Generate and open immediately:

doc-engine build --open

Rebuild on every save:

doc-engine build --watch

Use as Python module:

python -m doc_engine build README.md

Front Matter

Any Markdown file can open with a --- block to carry its own settings, so the document renders the same way for everyone β€” no flags to remember:

---
title: Payments API
subtitle: Integration Guide
author: Platform Team
template: technical
accent: teal
---

# Payments API

...

Supported keys: title, subtitle, author, date, template, accent, paper, and bib. A flag passed on the command line overrides the matching front-matter key, which in turn overrides the auto-detected value.


Watch Mode

Pass --watch to keep doc-engine running and rebuild the PDF whenever you save the source. It's the fastest way to tweak a template or accent and see the result:

doc-engine watch mode rebuilding on save
Every file the manifest names is watched, and a save rebuilds the whole document.
doc-engine build --watch --template modern --accent teal

The output path is chosen once when watch starts, then rewritten in place on each change. Press Ctrl+C to stop.


Diagrams

Fenced blocks tagged mermaid or svg become pictures instead of code:

```mermaid
flowchart LR
    Client --> API --> Ledger
```

Mermaid is rendered through an embedded JavaScript engine, so there is no Node install and no headless browser β€” it stays a plain pip install. A svg block is passed straight through, since Typst draws SVG natively.

If a diagram has a syntax error, the build stops and reports Mermaid's own message rather than producing a broken document.


Math

LaTeX math is translated into native Typst math, inline with $…$ and as a display block with $$…$$:

The quadratic formula is $x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$.

$$
P(A \mid B) = \frac{P(B \mid A)\,P(A)}{P(B)}
$$

Greek letters, relations, fractions, roots, sub- and superscripts, font commands, matrices, and cases are covered. Anything unrecognized passes through with its backslash removed, which lands on the right Typst symbol most of the time.

A $ that is not math stays untouched, so prices and shell variables survive: $10, $HOME, and export $PATH all render as written.


Alerts

A blockquote that opens with a marker becomes a coloured callout, matching what GitHub shows on the page:

> [!NOTE]
> Useful information worth knowing.

> [!WARNING]
> Something that needs attention.

NOTE, TIP, IMPORTANT, WARNING, and CAUTION are all recognized. A blockquote without a marker stays an ordinary quote.


Code Themes

Code blocks are highlighted with Typst's own colours by default. --code-theme swaps that for something else:

doc-engine build --code-theme github
doc-engine build --code-theme monochrome     # for printing in black and white
Theme Look
github GitHub's light palette
solarized Solarized light
monochrome Greys only β€” keeps code legible on a black-and-white printer

Any TextMate .tmTheme file works too, so a theme from your editor can be pointed at directly:

doc-engine build --code-theme ~/themes/my-editor.tmTheme

Cross-References

Inside a manifest build, a link from one included file to another becomes a jump within the PDF rather than a link to a file the reader does not have:

For the full picture see [the data model](model.md).

That resolves to the place where model.md was merged in. Links to anything outside the build β€” a URL, a file that is not part of the manifest β€” are left exactly as they are.


Multi-File Documents

A project that has outgrown a single file lists its parts in doc-engine.md, using ordinary Markdown links so the manifest still reads as a table of contents on GitHub:

---
title: Payments Platform
subtitle: Engineering Handbook
template: report
---

- [Overview](doc/overview.md)
- [Architecture](diagrams/architecture.mmd)
- [Schema](img/schema.svg)
- [References](bib/references.bib)

Then just build:

doc-engine build
doc-engine building a multi-file document
One manifest, one command: sections, a diagram, a figure, and a bibliography.

Each entry is handled by what it is:

Entry What happens
.md Appended as a section, headings intact
.mmd, .mermaid Rendered as a diagram at that point
.png, .svg, .jpg, … Placed as a captioned figure
.bib Registered as the bibliography for the document

Paths resolve against the manifest's folder, and every included file resolves its own images relative to itself β€” so a file builds the same way alone as it does inside the manifest. --watch follows every file the manifest names.


Project Configuration

Team defaults belong in a .doc-engine.toml next to the project, not repeated in every document:

[doc-engine]
template = "report"
accent = "teal"
paper = "us-letter"

A [tool.doc-engine] table in pyproject.toml works the same way. Precedence runs command-line flag, then front matter, then this file.

Every key it understands:

Key Values
template A built-in layout name, or a path to a .typ file
paper a3–a6, iso-b5, jis-b5, us-letter, us-legal, us-tabloid
accent A hex value or a colour name
author Author name, used instead of the Git user
bib Path to a .bib file
branding false hides the doc-engine attribution
code_theme github, solarized, monochrome, or a path to a .tmTheme
pdf_standard a-2b or a-3b
tall_images fit or split
fetch_images true downloads images linked by URL

Anything else in the table is ignored, so a typo cannot quietly change how a document is built.


Large Images

A picture that does not fit the text block is scaled down until it does, so nothing is ever clipped. For a tall diagram β€” a top-down flowchart, a long schema β€” scaling it onto one page can leave it unreadable, so it can be cut across pages at full size instead:

doc-engine build --tall-images split

Archival PDFs

For documents that have to stay readable for decades:

doc-engine build --pdf-standard a-2b

a-3b is also accepted, which additionally allows embedded attachments.


Templates

doc-engine ships with seven layouts. Switch with --template <name>, and recolor any of them with --accent.

Template Look
academic Serif IEEE-style report with cover page, table of contents, and running headers. The default.
modern Clean sans-serif layout with generous spacing and a left-aligned cover.
minimal No cover or table of contents β€” a compact title block, then straight into the content.
technical Bold layout with a filled accent banner and section markers. Good for engineering docs.
book Classic centered title page with chapter-style section breaks.
article A LaTeX paper: New Computer Modern, numbered sections, title block on page one.
report Roomy and easy on the eyes β€” 12pt on generous leading, wide margins, lots of air.
doc-engine build --template book
doc-engine build --template modern --accent rose

Accent colors take a hex value (#0ea5e9) or one of these names: blue, sky, indigo, violet, purple, red, rose, orange, amber, green, emerald, teal, slate, black.

Bring your own template

--template also accepts a path to a .typ file, so you can ship a house style without forking the project:

doc-engine build --template ./corporate.typ

The quickest way to start is to copy one of the files in doc_engine/templates/ and edit it. A template exposes a single setup_doc entry point, and the compiler passes it the document metadata:

#let setup_doc(
  title: "",
  subtitle: "",
  author: "Anonymous",
  date: datetime.today().display(),
  bibliography_file: none,
  accent: none,
  branding: true,
  version: "",
  body,
) = { ... }

Checking for Errors

Before compiling, doc-engine scans the Markdown for problems and reports them with the exact line and column, so you can jump straight to the fix:

README.md:42:8: error: link URL must not be empty
README.md:51:1: warning: image source is empty

Errors stop the build; warnings don't. Use --dry-run to run the check on its own without producing a PDF β€” handy in CI:

doc-engine build --dry-run

Architecture

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  README.md  β”‚
                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
                    β”‚   CLI Layer  β”‚  click + rich
                    β”‚  (cli.py)    β”‚  arg parsing, git detection
                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚                         β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
       β”‚  Converter   β”‚          β”‚   Compiler   β”‚
       β”‚(converter.py)β”‚          β”‚(compiler.py) β”‚
       β”‚              β”‚          β”‚              β”‚
       β”‚ Markdown AST β”‚          β”‚  Typst β†’ PDF β”‚
       β”‚  β†’ Typst     β”‚          β”‚  via typst-pyβ”‚
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
              β”‚                         β”‚
              β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
              └────► templates/   β—„β”€β”€β”€β”€β”€β”˜
                   β”‚   *.typ      β”‚
                   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
                   β”‚  output.pdf  β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Pipeline

Stage Module Responsibility
1. Input Resolution cli.py Locate Markdown file, detect Git metadata
2. Source Checking linter.py Report empty links and unclosed fences with line/column
3. Markdown Parsing converter.py Parse Markdown AST via mistune, emit Typst markup
4. Template Injection compiler.py Merge converted content with the selected template
5. PDF Compilation compiler.py Compile via typst Python bindings

How It Works

Markdown β†’ Typst Conversion

The converter module parses Markdown using mistune and generates equivalent Typst markup:

Markdown Typst Output
# Heading = Heading
**bold** *bold*
*italic* _italic_
`code` `code`
[text](url) #link("url")[text]
- item - item
1. item + item
- [x] task rendered checkbox
text[^1] #footnote[...]
![alt](local.png) #image("local.png")
<img src="local.png" width="300"> Embedded image, using the same path as Markdown images
> blockquote #block(...)
--- #line(...)

Special characters (#, $, @, *, _, etc.) are automatically escaped to prevent Typst interpretation.

A width or height on an HTML image is read as CSS pixels, a 96th of an inch each, and no picture is drawn wider than the text block however large the number is. Percentages are ignored, since fitting the text block is already the default.

PDF Templates

Each template lives in doc_engine/templates/ and exposes the same setup_doc entry point, so the compiler can swap between them with --template. The default academic template provides:

  • Cover page with title, author, and date
  • Table of contents with depth-3 navigation
  • Running headers with document title and author
  • Page footer with page numbers and engine attribution
  • Code blocks with rounded corners and subtle borders
  • Heading hierarchy with accent-colored H2 sections

The other templates (modern, minimal, technical, book) keep the same content but change the fonts, layout, and cover. The accent color is injected at compile time, so --accent recolors any of them.


Project Structure

doc-engine-cli/
β”œβ”€β”€ doc_engine/
β”‚   β”œβ”€β”€ __init__.py          # Package version
β”‚   β”œβ”€β”€ __main__.py          # python -m doc_engine entrypoint
β”‚   β”œβ”€β”€ cli.py               # Click-based CLI + Git detection
β”‚   β”œβ”€β”€ help.py              # Rich help screens
β”‚   β”œβ”€β”€ config.py            # .doc-engine.toml project settings
β”‚   β”œβ”€β”€ settings.py          # flag / front matter / project precedence
β”‚   β”œβ”€β”€ frontmatter.py       # Leading --- metadata block
β”‚   β”œβ”€β”€ manifest.py          # doc-engine.md multi-file builds
β”‚   β”œβ”€β”€ converter.py         # Markdown β†’ Typst transpiler
β”‚   β”œβ”€β”€ latex.py             # LaTeX math β†’ Typst math
β”‚   β”œβ”€β”€ diagrams.py          # Mermaid and SVG blocks
β”‚   β”œβ”€β”€ images.py            # Cutting pictures taller than a page
β”‚   β”œβ”€β”€ remote.py            # Downloading linked images
β”‚   β”œβ”€β”€ compiler.py          # Typst β†’ PDF compilation engine
β”‚   β”œβ”€β”€ linter.py            # Source checks (line/column reporting)
β”‚   └── templates/
β”‚       β”œβ”€β”€ academic.typ     # Default IEEE-style report
β”‚       β”œβ”€β”€ article.typ      # LaTeX paper, numbered sections
β”‚       β”œβ”€β”€ report.typ       # Roomy and legible
β”‚       β”œβ”€β”€ modern.typ       # Clean sans-serif layout
β”‚       β”œβ”€β”€ minimal.typ      # Compact, no cover page
β”‚       β”œβ”€β”€ technical.typ    # Accent banner + section markers
β”‚       └── book.typ         # Centered title page, chapter breaks
β”‚   └── themes/
β”‚       β”œβ”€β”€ github.tmTheme    # Syntax highlighting themes
β”‚       β”œβ”€β”€ solarized.tmTheme
β”‚       └── monochrome.tmTheme
β”œβ”€β”€ tests/                    # 181 tests across every module
β”œβ”€β”€ pyproject.toml            # Package configuration + dependencies
β”œβ”€β”€ LICENSE                   # MIT License
β”œβ”€β”€ .gitignore
└── README.md

Dependencies

Package Purpose License
click CLI framework BSD-3
rich Terminal formatting and progress indicators MIT
mistune Markdown parser (pure Python) BSD-3
typst Typst compiler bindings Apache-2.0
mermaidx Mermaid rendering without Node MIT
pillow Cutting pictures taller than a page MIT-CMU

All dependencies are pure Python β€” no external binaries (Pandoc, LaTeX, etc.) are required.


Development

Setup

git clone https://github.com/leonardosalasd/doc-engine-cli.git
cd doc-engine-cli
pip install -e ".[dev]"

Run Tests

python -m pytest tests/ -v

Project Commands

# Generate PDF from this project's README
python -m doc_engine build

# Run with verbose error output
python -m doc_engine build README.md -o docs_output.pdf

Docker

A container image is published to GitHub Container Registry on every release. Mount your project into /workspace and run build as usual:

docker run --rm -v "$PWD:/workspace" ghcr.io/leonardosalasd/doc-engine-cli build

The entrypoint is doc-engine, so you can pass any command or flag:

docker run --rm -v "$PWD:/workspace" ghcr.io/leonardosalasd/doc-engine-cli build --template modern --accent teal

Supported Markdown Elements

  • Headings (H1–H6)
  • Bold, italic, strikethrough
  • Inline code and fenced code blocks (with language hints)
  • Links
  • Ordered and unordered lists
  • Nested lists
  • Blockquotes
  • Tables
  • Horizontal rules
  • Line breaks (<br>)
  • Task lists (- [x] / - [ ])
  • Footnotes ([^1])
  • Local images, and remote ones with --fetch-images
  • Raw HTML <img> tags (inline or block), sized by width and height in pixels
  • Math blocks (LaTeX $…$ and $$…$$)
  • Mermaid and SVG diagram blocks
  • GitHub alerts (> [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION])

Roadmap

  • Template selection via --template flag
  • Configurable accent color via --accent
  • Source error checking with line/column and --dry-run
  • User-supplied template files (point --template at a path)
  • YAML front-matter support for metadata override
  • Local image embedding
  • Watch mode for continuous rebuilds
  • Math expressions (LaTeX-style $...$)
  • Multi-file documentation merge
  • Mermaid diagram rendering
  • Image downloading and embedding for remote URLs
  • PDF/A compliance for archival
  • Page size selection
  • Project-level configuration file
  • Syntax highlighting themes for code blocks
  • Cross-references between documents

Sponsor

doc-engine-cli is written and maintained by one person, in the open, and it is free under the MIT licence for everyone β€” no paid tier, no telemetry, no upsell. If it saves you the afternoon you would have spent fighting LaTeX, you can put something behind it:

Sponsorship pays for the time that goes into the parts nobody sees: testing on Windows, chasing a rendering bug across five Python versions, answering issues, and keeping the release quality where it is.

It is entirely optional. Starring the repository, reporting a bug you hit, or telling someone the tool exists all help too, and cost nothing.


Contributing

Contributions are welcome β€” bug reports, documentation fixes, new templates, and features. The contributing guide covers setup, testing across Python versions, code style, and how to add a template.

Contributing guide Setup, tests, style, pull requests
Code of conduct Expected behavior in community spaces
Security policy Reporting a vulnerability privately
Support Where to ask questions and report problems

Questions belong in Discussions; bugs belong in Issues.


License

This project is licensed under the MIT License.


Built with Typst Β· Parsed with mistune Β· Styled with Rich

About

A high-performance CLI to transform Markdown into premium, academic-grade PDFs via Typst. Zero configuration required.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

46 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages