mopac-pdf v0: markdown to beautiful PDFs via digest-pinned typst

Go CLI that turns the markdown currency of this stack (Redmine notes,
Discourse posts, briefing output) into typeset PDFs: two embedded typst
templates (report with title page/TOC/headers, dense brief), front-matter
(title/subtitle/author/date/classification/template), GFM tables, and
bar charts rendered in pure Go (go-chart) from fenced chart data blocks.
Engine is a prebuilt typst 0.15.1 container pinned by digest and run
--network none; PDF bytes to stdout or -o. Exit codes 0/1/2.

All dev in docker (dev.sh/Makefile); unit tests + golden typst fixtures
plus a host-side smoke against the real engine container.

Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-29 05:44:24 -05:00
parent 97c8e340ab
commit 10e930e46d
147 changed files with 30793 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
package chart
import (
"bytes"
"image"
_ "image/png"
"testing"
"ukrrs.com/mopac/pdf/internal/markdown"
)
func TestBarPNG(t *testing.T) {
c := &markdown.Chart{
Type: "bar",
Title: "Revenue",
Unit: "$K",
Labels: []string{"Q1", "Q2", "Q3"},
Values: []float64{10, 20, 15},
}
png, err := Bar(c)
if err != nil {
t.Fatalf("render: %v", err)
}
if !bytes.HasPrefix(png, []byte{0x89, 'P', 'N', 'G'}) {
t.Fatalf("not a PNG: % x", png[:4])
}
img, _, err := image.Decode(bytes.NewReader(png))
if err != nil {
t.Fatalf("decode: %v", err)
}
b := img.Bounds()
if b.Dx() < 300 || b.Dy() < 200 {
t.Fatalf("unexpected dimensions %dx%d", b.Dx(), b.Dy())
}
}
func TestBarRejectsUnknownType(t *testing.T) {
c := &markdown.Chart{Type: "pie", Labels: []string{"a"}, Values: []float64{1}}
if _, err := Bar(c); err == nil {
t.Fatal("pie must be rejected in v0")
}
}
func TestBarRejectsEmpty(t *testing.T) {
if _, err := Bar(nil); err == nil {
t.Fatal("nil chart must be rejected")
}
c := &markdown.Chart{Type: "bar"}
if _, err := Bar(c); err == nil {
t.Fatal("chart without data must be rejected")
}
}