Files

175 lines
7.1 KiB
Markdown
Raw Permalink Normal View History

# Scriptorium
Scriptorium renders scientific documents server-side in Go: Markdown into
HTML, TeX mathematics into MathML Core, and Mermaid flowchart and sequence
diagrams into SVG. Three engines, each written from scratch in this
repository on the Go standard library alone, producing markup the reader's
browser already knows how to draw: no JavaScript runs on the page that reads
the result.
## The strengths, each with its proof
**Complete Markdown.** The engine renders the full CommonMark 0.31.2
grammar plus the GitHub Flavored Markdown extensions. Measured against the
official suites: 649 of 652 CommonMark examples pass, and the three
exceptions are bare URLs and email addresses that the GFM extended
autolinks turn into links, the same trade GitHub's own reference
implementation makes; all 23 extension examples of the GFM specification
pass byte for byte. Footnotes and definition lists come on top, rendered
in the shapes GitHub and Pandoc established.
**Complete mathematics.** Every one of the 391 mathematical symbols in the
KaTeX coverage table maps to MathML Core, verified by a systematic diff
against KaTeX's own source tables, together with fractions, radicals,
scripts with movable limits, stretchy delimiters, the amsmath environments,
accents, styles, colours, extensible arrows and bounded macros. Where a
KaTeX-compatible renderer needs JavaScript in the reader's browser,
scriptorium emits MathML Core that Chromium, Firefox and Safari draw
natively.
**Deterministic.** The same input produces byte-identical output on every
call, in every process, on every machine. Diagram layouts break ties by the
order of appearance and never by map iteration; tests assert byte equality
across repeated renders in all three engines. Output is safe to cache, to
diff and to sign.
**Zero dependencies.** The Go standard library alone, direct and
transitive. There is no `go.sum`, because there is nothing to sum: no
parser to supply-chain, no renderer to version-pin, no JavaScript bundle to
ship. Compiles wherever Go compiles.
**Honest about its edges.** A construct outside a mappable surface is never
dropped and never guessed at: mathematics degrades in place, the unknown
command standing as its verbatim source inside a marked element while the
rest of the expression still renders, and a diagram type outside the two
grammars is refused with an error naming the type and the source line.
**Ready for servers.** The engines hold no state between calls, so every
function is safe for concurrent use. Rendering never fails on malformed
input: Markdown and mathematics always produce output, and a diagram error
tells the author the line to fix.
**No sanitisation, by contract.** The library renders trusted input and
writes the output verbatim; whether the result may reach a given audience
is the consumer's policy. A consumer of untrusted input keeps its own
sanitiser and applies it to the rendered result.
## A taste
```go
package main
import (
"os"
"sourcedock.dev/petrbalvin/scriptorium"
)
func main() {
os.Stdout.Write(scriptorium.Render([]byte(
"# Measured, not guessed\n\nA *claim* with [a source](/uri) and some `code`.\n")))
os.Stdout.Write(scriptorium.RenderMathDisplay([]byte(`\sum_{i=1}^{n} \frac{1}{i^2}`)))
svg, err := scriptorium.RenderDiagram([]byte("flowchart LR\n A[Input] --> B{Model} --> C[Result]\n"))
if err != nil {
panic(err)
}
os.Stdout.Write(svg)
}
```
The first call prints exactly:
```html
<h1>Measured, not guessed</h1>
<p>A <em>claim</em> with <a href="/uri">a source</a> and some <code>code</code>.</p>
```
The second prints a complete MathML Core element, the summation limits
above and below the operator the way display mathematics sets them:
```html
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><munderover><mo movablelimits="true">∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mfrac><mn>1</mn><msup><mi>i</mi><mn>2</mn></msup></mfrac></math>
```
The third returns a self-contained SVG document of the flowchart, every
element positioned by the library's own layout in whole pixels: ranks by
longest path, ordering by barycentre sweeps.
## The engines
### Markdown
CommonMark 0.31.2 in full, with the GFM extensions: tables with per column
alignment, strikethrough, task lists and extended autolinks (`www.`,
`http(s)://`, `ftp://` and bare email addresses, with the punctuation,
parenthesis and entity trimming rules of the specification), plus footnotes
and definition lists. Tables render in the exact shape of GitHub's
reference implementation.
### Mathematics
The standard TeX command surface that maps to MathML Core, measured against
the KaTeX coverage list: the Greek letters including the variants, the
relation and operator tables including negations and the colon relations,
the arrows including the extensible `\x` family, the big operators, the
delimiters, and the letter-like symbols; the amsmath environments (`matrix`
and every delimited variant, `cases`, `aligned`, `align`, `gather`,
`equation`, `array`, `substack`); accents and over and under lines; the
font commands; spacing; colours; and bounded `\newcommand`, `\def` and
`\DeclareMathOperator` macros with expansion limits that turn a runaway
definition into an honest error rather than a hang.
### Diagrams
The two Mermaid grammars the forge's documents actually use, a coverage
measured across every repository on the forge. Flowchart (including the
historical `graph` alias): every node shape, every edge kind with labels,
branching, subgraphs with their own direction, classes and styles.
SequenceDiagram: participants and actors, all arrow kinds, notes,
activations, the `alt`/`opt`/`loop`/`par`/`critical`/`break` blocks,
coloured regions, `autonumber` and dividers. Everything else is refused
with a clear error naming the type.
## The API
| Function | Renders | Fails |
|---|---|---|
| `Render(source []byte) []byte` | Markdown to HTML | never |
| `RenderMath(source []byte) []byte` | TeX to inline MathML Core | never |
| `RenderMathDisplay(source []byte) []byte` | TeX to display MathML Core | never |
| `RenderDiagram(source []byte) ([]byte, error)` | Mermaid to SVG | on an unsupported type or a malformed statement |
| `SupportedDiagram(kind string) bool` | reports the Mermaid scope | |
## Install
```sh
go get sourcedock.dev/petrbalvin/scriptorium
```
Requires Go 1.27.1 or newer.
## Documentation
- [docs/API.md](docs/API.md): the surface in full, with the exact
boundary of the mathematics surface and every command that degrades
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): how the engines are built
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): the workflow and the recipes
- [CHANGELOG.md](CHANGELOG.md): the user-visible history
- [CONTRIBUTING.md](CONTRIBUTING.md): how to contribute
- [SECURITY.md](SECURITY.md): how to report a vulnerability
## Development
```sh
just build # compile every package
just test # the test suite with the coverage floor, under a memory fence
just fmt # format
just gates # the definition of done: build, format, vet, tests, race
```
## Licence
MIT. See [LICENSE](LICENSE).
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)