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
+42
View File
@@ -0,0 +1,42 @@
package chart
import (
"bytes"
"errors"
"image"
"image/png"
)
// RGBACollector is a render target for a chart.
type RGBACollector interface {
SetRGBA(i *image.RGBA)
}
// ImageWriter is a special type of io.Writer that produces a final image.
type ImageWriter struct {
rgba *image.RGBA
contents *bytes.Buffer
}
func (ir *ImageWriter) Write(buffer []byte) (int, error) {
if ir.contents == nil {
ir.contents = bytes.NewBuffer([]byte{})
}
return ir.contents.Write(buffer)
}
// SetRGBA sets a raw version of the image.
func (ir *ImageWriter) SetRGBA(i *image.RGBA) {
ir.rgba = i
}
// Image returns an *image.Image for the result.
func (ir *ImageWriter) Image() (image.Image, error) {
if ir.rgba != nil {
return ir.rgba, nil
}
if ir.contents != nil && ir.contents.Len() > 0 {
return png.Decode(ir.contents)
}
return nil, errors.New("no valid sources for image data, cannot continue")
}