Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,7 @@ marimo/_static/
marimo/_lsp/
__marimo__/

refs.bib
refs.bib

# A parked VS Code extension, kept locally. See vscode/NOT-PUBLISHED.md
vscode/
27 changes: 22 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ locally is faster. If you have [uv](https://github.com/astral-sh/uv):

```bash
uv run --python 3.10 --no-project --with mistune --with click --with rich \
--with typst --with pytest python -m pytest tests/ -q
--with typst --with mermaidx --with pillow --with tomli --with pytest \
python -m pytest tests/ -q
```

Swap `3.10` for any version in the supported range.
Expand Down Expand Up @@ -100,7 +101,7 @@ feat: add support for A4 page size
fix: prevent crash on legacy Windows consoles
docs: document the --watch flag
ci: test on Python 3.14
chore: bump version to 1.1.1
chore: bump version to 2.0.0
```

Write the subject in the imperative mood, under ~72 characters. If the change
Expand All @@ -118,9 +119,9 @@ anything a future reader would need in order to not undo it by accident.

Every pull request is reviewed by the repository owner
([CODEOWNERS](.github/CODEOWNERS)), and CI must be green before merging. CI runs
the test suite across every supported Python version and operating system, plus
a smoke job that installs the built package and drives the CLI from a clean
directory.
the test suite across every supported Python version and operating system, a
smoke job that installs the built package and drives the CLI from a clean
directory, a lint and format check, and a build of the Docker image.

In the description, explain what changed and why. If it fixes an open issue,
reference it with `Fixes #123`.
Expand All @@ -143,6 +144,8 @@ passes it the document metadata:
accent: none,
branding: true,
version: "",
paper: "a4",
..options,
body,
) = { ... }
```
Expand All @@ -152,8 +155,14 @@ should:

- Honor `accent` when it is not `none`, and fall back to its own default color.
- Honor `branding: false` by hiding the `doc-engine` attribution.
- Pass `paper` through to `set page`, rather than hardcoding a size.
- Render `subtitle` only when it is not empty.
- Render the bibliography when `bibliography_file` is not `none`.
- Keep the `..options` sink, so a later release can add an option without
breaking templates that have not been updated.
- End every font stack in one Typst ships — `New Computer Modern`,
`Libertinus Serif`, or `DejaVu Sans Mono` — so the document looks the same
inside a bare container as it does on a laptop full of fonts.

Templates are discovered automatically, so dropping the file in is enough for
`--template <name>` to find it. Add it to the templates table in the README and
Expand All @@ -166,9 +175,17 @@ to the test in `tests/test_cli.py` that asserts the shipped set.
| Module | Responsibility |
|---|---|
| `cli.py` | Argument parsing, Git detection, watch loop, output naming |
| `help.py` | The Rich help screens |
| `settings.py` | Deciding between a flag, front matter, and the project file |
| `config.py` | Reading `.doc-engine.toml` |
| `frontmatter.py` | Leading `---` metadata block |
| `manifest.py` | Building one document out of several files |
| `linter.py` | Source checks reported with line and column |
| `converter.py` | Markdown AST → Typst markup |
| `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` | Template injection and PDF compilation |

Changes usually belong in exactly one of these. If a change needs to touch all
Expand Down
151 changes: 122 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/doc-engine-cli?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/doc-engine-cli)
[![Typst](https://img.shields.io/badge/Powered_by-Typst-239DAD.svg?logo=typst&logoColor=white)](https://typst.app/)
[![Tests](https://github.com/leonardosalasd/doc-engine-cli/actions/workflows/tests.yml/badge.svg)](https://github.com/leonardosalasd/doc-engine-cli/actions/workflows/tests.yml)
[![Sponsor](https://img.shields.io/badge/Sponsor-%E2%9D%A4-EA4AAA.svg?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/leonardosalasd)

Transform any `README.md` into a premium, print-ready PDF report — no configuration, no templates, no LaTeX.

Expand All @@ -21,34 +22,18 @@ Transform any `README.md` into a premium, print-ready PDF report — no configur
pipx install doc-engine-cli
```

<br>

<a href="https://github.com/sponsors/leonardosalasd">
<img src="https://img.shields.io/badge/Sponsor%20this%20project-%E2%9D%A4-EA4AAA?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="Sponsor doc-engine-cli on GitHub Sponsors" height="34"/>
</a>

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

---

</div>

> [!NOTE]
> ### v2.0.0 is finished and looking for testers
>
> All of v2.0.0 is written and merged here — diagrams, math, multi-file
> documents, seven layouts, page sizes, and the rest. It is **not on PyPI yet**,
> because I would rather find the rough edges before publishing than after.
> `pipx install doc-engine-cli` still gives you the stable **1.1.1**.
>
> **Try it and help me break it:**
>
> ```bash
> git clone https://github.com/leonardosalasd/doc-engine-cli.git
> cd doc-engine-cli
> pip install -e .
> doc-engine build
> ```
>
> Everything in this README describes v2.0.0 and works on that checkout. If
> something misbehaves, an [issue](https://github.com/leonardosalasd/doc-engine-cli/issues/new)
> or a note in [Discussions](https://github.com/leonardosalasd/doc-engine-cli/discussions)
> is genuinely useful — that feedback is what decides when this ships.
>
> The release follows once it has been through real documents on real machines.

## Overview

**doc-engine-cli** is a developer-first CLI tool that converts Markdown files into professionally styled PDF documents using [Typst](https://typst.app/) 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.
Expand All @@ -72,6 +57,7 @@ That's it. Zero configuration required.
| **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. |
Expand Down Expand Up @@ -145,7 +131,8 @@ doc-engine build path/to/file.md -o output.pdf -t "Custom Title" -a "Author Name

```
doc-engine build [INPUT_FILE] Convert a Markdown file into a PDF
doc-engine info Show version, repository, and templates
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
```
Expand All @@ -165,6 +152,7 @@ doc-engine --help Show all commands and flags
| `--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. |
Expand Down Expand Up @@ -258,9 +246,9 @@ Pass `--watch` to keep `doc-engine` running and rebuild the PDF whenever you sav
the source. It's the fastest way to tweak a template or accent and see the result:

<div align="center">
<img src="assets/features-v1.1.gif" alt="doc-engine watch mode rebuilding on save" width="800"/>
<img src="assets/features-watch.gif" alt="doc-engine watch mode rebuilding on save" width="800"/>
<br>
<em>Metadata read from front matter, rebuilt live on every save.</em>
<em>Every file the manifest names is watched, and a save rebuilds the whole document.</em>
</div>

```bash
Expand Down Expand Up @@ -313,6 +301,65 @@ 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:

```markdown
> [!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:

```bash
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:

```bash
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:

```markdown
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
Expand Down Expand Up @@ -376,6 +423,24 @@ 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
Expand Down Expand Up @@ -562,6 +627,7 @@ doc-engine-cli/
│ ├── 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
Expand All @@ -579,6 +645,10 @@ doc-engine-cli/
│ ├── 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
Expand Down Expand Up @@ -664,6 +734,7 @@ docker run --rm -v "$PWD:/workspace" ghcr.io/leonardosalasd/doc-engine-cli build
- [x] Local images, and remote ones with `--fetch-images`
- [x] Math blocks (LaTeX `$…$` and `$$…$$`)
- [x] Mermaid and SVG diagram blocks
- [x] GitHub alerts (`> [!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, `[!CAUTION]`)

---

Expand All @@ -683,8 +754,30 @@ docker run --rm -v "$PWD:/workspace" ghcr.io/leonardosalasd/doc-engine-cli build
- [x] PDF/A compliance for archival
- [x] Page size selection
- [x] Project-level configuration file
- [ ] Syntax highlighting themes for code blocks
- [ ] Cross-references between documents
- [x] Syntax highlighting themes for code blocks
- [x] 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:

<div align="center">
<a href="https://github.com/sponsors/leonardosalasd">
<img src="https://img.shields.io/badge/Sponsor%20on%20GitHub-%E2%9D%A4-EA4AAA?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="Sponsor on GitHub" height="38"/>
</a>
</div>

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.

---

Expand Down
22 changes: 15 additions & 7 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ pipx upgrade doc-engine-cli

| Version | Supported |
|---|---|
| 1.1.x | Yes |
| 1.0.x | Please upgrade |
| < 1.0 | No |
| 2.0.x | Yes |
| 1.1.x | Please upgrade |
| < 1.1 | No |

---

Expand Down Expand Up @@ -76,22 +76,30 @@ This is a command-line tool that reads Markdown and templates from your machine
and produces a PDF. Reports are most relevant when they involve:

- Reading or writing files outside the intended input and output paths, for
example through a crafted image path, bibliography path, or template path.
example through a crafted image path, bibliography path, template path,
syntax theme path, or an entry in a `doc-engine.md` manifest.
- Arbitrary code or command execution triggered by a Markdown file, a `.typ`
template, or front-matter values.
template, front-matter values, a `.doc-engine.toml`, or a Mermaid diagram,
which is rendered by running JavaScript in an embedded engine.
- Anything reachable through `--fetch-images`, which is the only feature that
makes a network request. It is off unless asked for, and limited to http and
https with a timeout and a size ceiling.
- Crafted input that causes the compiler to hang indefinitely or exhaust memory.

Out of scope:

- Vulnerabilities in dependencies — report those upstream to
[`typst`](https://github.com/messense/typst-py),
[`mistune`](https://github.com/lepture/mistune),
[`mermaidx`](https://github.com/MohammadRaziei/mermaidx),
[`pillow`](https://github.com/python-pillow/Pillow),
[`click`](https://github.com/pallets/click), or
[`rich`](https://github.com/Textualize/rich). If a dependency issue affects
this project specifically, a report here is still useful.
- Crashes and malformed output that carry no security impact. Those are ordinary
bugs — please [open an issue](https://github.com/leonardosalasd/doc-engine-cli/issues/new).

A `.typ` template is executable input by design: pointing `--template` at a file
means running it. Treat templates from untrusted sources the same way you would
treat any other code you download and run.
means running it. The same goes for a `doc-engine.md` manifest, which decides
which files on your machine are read into the document. Treat both from
untrusted sources the way you would treat any other code you download and run.
Binary file modified assets/config.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/doc-engine2.gif
Binary file not shown.
Binary file removed assets/features-v1.1.gif
Binary file not shown.
Binary file added assets/features-watch.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading