md2img

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: MIT Imports: 19 Imported by: 0

README

md2img

Convert Markdown to styled PNG images. No browser, no Python — just a Go binary.

markdown → goldmark (parser) → Go image (render) → PNG

Install

Homebrew (macOS/Linux)
brew install jmaciasluque/tap/md2img
Pre-built binaries

Download the latest release for your platform from the Releases page.

From source
git clone https://github.com/jmaciasluque/md2img.git
cd md2img
make build    # → ./md2img
make install  # → ~/go/bin/md2img
With Go
go install github.com/jmaciasluque/md2img/cmd/md2img@latest

No external dependencies. Just a single Go binary. TTF fonts are loaded from your system (Arial, Courier New, Times New Roman on macOS).

Usage

# From stdin
echo "| Name | Score |\n|------|-------|\n| Alice | 95 |" | md2img -o scores.png

# Explicit stdin marker
echo "# Hello" | md2img -o hello.png -

# From file
md2img -o output.png input.md

# Default output: /tmp/md2img_output.png
echo "# Hello" | md2img

# Trim whitespace (tight crop)
echo "| A | B |" | md2img -o tight.png -trim

CLI Flags

Output
Flag Description Default
-o, --output Output file path /tmp/md2img_output.png
-trim, --trim Auto-crop whitespace from PNG output false
-trim-padding, --trim-padding Padding around content after trim (mm) 5
-dpi, --dpi Image resolution (DPI) 200
-version, --version Print version
Font
Flag Description Default
-font, --font Body font family (Helvetica, Times, Courier) Helvetica
-font-file Body font TTF/OTF path
-font-size, --font-size Body font size (points) 14
-heading-font, --heading-font Heading font (empty = same as body) (same as body)
-heading-font-file Heading font TTF/OTF path
Page Layout
Flag Description Default
-page-w, --page-w Page width in mm 210 (A4)
-page-h, --page-h Page height in mm 297 (A4)
-margin, --margin All margins in mm 15
Table
Flag Description Default
-table-full-width, --table-full-width Stretch table to fill available width false (auto-width)
-table-header-font Table header font (same as body)
-table-header-font-file Table header font TTF/OTF path
-table-header-size Table header font size 12

Tables auto-size to fit their content by default. Use -table-full-width to stretch across the full width between margins.

Theme
Flag Description Default
-theme Theme preset: light, dark, github, slack light
Colors

All color flags accept hex values: #333366, 333366, or shorthand #fff.

Flag Description Default
-text-color Body text color #282828
-link-color Link text color #2850B4
-heading-color Heading text color #282850
-table-header-bg Table header background #323250
-table-header-fg Table header text color #C8C8FF
-table-row-even Even table row background #F5F5FA
-table-row-odd Odd table row background #FFFFFF
-code-bg Code block background #F0F0F0
-code-font Code block font Courier
-code-font-file Code block font TTF/OTF path
-code-font-size Code block font size 11
-blockquote-line-color Blockquote left border #6464C8
-blockquote-text-color Blockquote text color #646464
-hr-color Horizontal rule color #B4B4B4
Examples
# Dark theme table
echo "| Name | Score |\n|------|-------|\n| Alice | 95 |" | md2img -theme dark -o dark_table.png

# US Letter, high resolution
md2img -o report.png -page-w 215.9 -page-h 279.4 -dpi 300 report.md

# Trim with custom padding (in mm)
echo "| A | B |" | md2img -o padded.png -trim -trim-padding 10

# Times font, large text
md2img -o big.png -font Times -font-size 16 -heading-font Helvetica input.md

# Use a specific Unicode-capable font file
md2img -o unicode.png -font-file /path/to/NotoSans-Regular.ttf input.md

# Full-width table (stretches to fill page)
echo "| A | B |\n|---|---|\n| 1 | 2 |" | md2img -o wide.png -table-full-width

As a library

import md2img "github.com/jmaciasluque/md2img"

// Simple usage
err := md2img.Render("# Hello\n\nWorld.", "output.png")

// With custom config
cfg := md2img.DefaultConfig()
cfg.DPI = 300
cfg.FontFamily = "Times"
cfg.TableHeaderBg = md2img.Color{R: 45, G: 55, B: 72}
cfg.TableHeaderFg = md2img.Color{R: 226, G: 232, B: 240}
cfg.Trim = true  // auto-crop whitespace

err := md2img.RenderWithConfig("# Report\n\n| A | B |\n|---|---|\n| 1 | 2 |", "report.png", cfg)

// Render without touching the filesystem
img, err := md2img.RenderImage("# Preview", cfg)
err = md2img.RenderPNG(w, "# Stream me", cfg)
Color helpers
// Parse hex colors
c, err := md2img.HexToColor("#333366")

// Or construct directly
c := md2img.Color{R: 51, G: 51, B: 102}

Chat and AI agents

Matrix, Slack, and most chat platforms don't render HTML tables — they weren't built for structured data. If you're an AI agent that needs to present a comparison or summary in a conversation, you're stuck with code blocks (ugly, no alignment) or images.

md2img fills that gap: markdown in, styled PNG out, send it as a message attachment.

# Quick table for a chat message
echo "| Model      | Speed    | Quality |
|------------|----------|---------|
| Qwen3-14B  | 11 tok/s | Good    |
| Gemma3-12B | 13 tok/s | Good    |" | md2img -trim -o /tmp/table.png

# Then send MEDIA:/tmp/table.png in your message

Works for longer reports too:

cat << 'EOF' | md2img -trim -o /tmp/report.png
## Weekly Summary

| Task            | Status  | Hours |
|-----------------|---------|-------|
| Blog post       | Done    | 4     |
| API refactor    | In progress | 6 |
| Deploy staging  | Blocked | 0     |
EOF

Supported Markdown

Element Rendering
Headers (H1–H6) Bold, sized proportionally
Tables Auto-sized columns (or full-width), configurable header/row colors
Table alignment Left, center, and right alignment markers
Bullet lists * prefix, including nested lists
Numbered lists 1. 2. 3. prefix
Task lists [x] and [ ] markers
Code blocks Monospace font, configurable background
Blockquotes Configurable left border, italic
Horizontal rules Configurable color and thickness
Bold / italic Supported inline within paragraphs
Links Colored and underlined link text
Strikethrough Horizontal strike through text
Inline code Monospace font at body text size

Limitations

  • Font dependent Unicode — glyphs render when the selected system/custom font supports them. Unsupported glyphs fall back to ASCII placeholders.
  • No inline images — text-based rendering only.

Benchmarks

No external dependencies keeps rendering simple. These results are from an Apple M4 at Go 1.26.2:

BenchmarkRenderSimple           6.0ms    16.6MB/op
BenchmarkRenderTable           14.5ms    17.1MB/op
BenchmarkRenderComplex         39.5ms    22.2MB/op
BenchmarkRenderDPI100          18.2ms     7.2MB/op
BenchmarkRenderDPI300          68.5ms    47.0MB/op
BenchmarkRenderTrimmed         46.6ms    22.2MB/op
BenchmarkRenderInline           6.6ms    16.7MB/op
BenchmarkRenderFullWidthTable  20.9ms    19.2MB/op

Project Structure

md2img/
├── cmd/md2img/     # CLI entry point
│   ├── main.go
│   └── main_test.go
├── render.go       # Direct-to-image rendering engine + Config (library API)
├── fonts.go        # TTF font loading with system fallback
├── trim.go         # Auto-crop whitespace
├── theme.go        # Built-in theme presets
├── sanitize.go     # Unicode → ASCII mapping
├── sanitize_test.go
├── render_test.go
├── bench_test.go
├── Makefile
├── .goreleaser.yaml
├── .github/workflows/
│   ├── ci.yml      # Build + test on macOS & Ubuntu
│   ├── bench.yml   # Benchmark suite with benchstat
│   └── release.yml # Multi-platform release builds
└── README.md

How It Works

  1. Parsegoldmark parses Markdown into an AST (with GFM table support)
  2. Render — Direct rendering to image.RGBA using golang.org/x/image for TTF font rendering
  3. Output — PNG encoding with optional auto-crop

The binary is a few MB depending on OS and architecture. No external dependencies — fonts are loaded from your system.

Development

# Run tests
make test

# Run go vet
make vet

# Build
make build

# Install locally
make install

# Run benchmarks
make bench

# Validate release configuration locally
make release-snapshot

# Compare against a previous run
go test -bench=. -benchmem -count=5 ./... | tee new.txt
benchstat old.txt new.txt

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Version = "dev"

Version is set at build time via ldflags.

Functions

func ApplyTheme added in v1.4.0

func ApplyTheme(cfg *Config, name string) error

ApplyTheme applies a named visual preset to cfg.

func Render

func Render(input, output string) error

Render converts markdown input to a PNG file at the given output path.

func RenderImage added in v1.4.0

func RenderImage(input string, cfg Config) (image.Image, error)

RenderImage converts markdown input to an image using the given configuration.

func RenderPNG added in v1.4.0

func RenderPNG(w io.Writer, input string, cfg Config) error

RenderPNG converts markdown input to PNG and writes it to w.

func RenderWithConfig added in v1.1.0

func RenderWithConfig(input, output string, cfg Config) error

RenderWithConfig converts markdown input to a PNG file using the given configuration.

Types

type Color added in v1.1.0

type Color struct {
	R, G, B int
}

Color represents an RGB color with values 0–255.

func HexToColor added in v1.1.0

func HexToColor(s string) (Color, error)

HexToColor parses a hex color string like "#333366" or "333366" into a Color.

type Config added in v1.1.0

type Config struct {
	// Font
	FontFamily string  // "Helvetica", "Times", or "Courier"
	FontSize   float64 // Body text size in points
	FontFile   string  // Optional TTF/OTF file for body text

	// Page
	PageWidth    float64 // Page width in mm (default: 210 = A4)
	PageHeight   float64 // Page height in mm (default: 297 = A4)
	MarginTop    float64 // Top margin in mm
	MarginLeft   float64 // Left margin in mm
	MarginRight  float64 // Right margin in mm
	MarginBottom float64 // Bottom margin in mm

	// Text colors
	TextColor Color // Default body text color
	LinkColor Color // Link text color

	// Heading colors and sizes
	HeadingColor Color
	HeadingSizes [6]float64
	HeadingFont  string // Heading font family override
	HeadingFile  string // Optional TTF/OTF file for headings
	HeadingBold  bool   // Render headings in bold (default: true)

	// Table
	TableHeaderBg   Color
	TableHeaderFg   Color
	TableHeaderFont string
	TableHeaderFile string
	TableHeaderSize float64
	TableCellHeight float64
	TableAutoWidth  bool // size columns to fit content
	TableRowEven    Color
	TableRowOdd     Color

	// Code block
	CodeBg         Color
	CodeFont       string
	CodeFontFile   string
	CodeFontSize   float64
	CodeLineHeight float64

	// Blockquote
	BlockquoteLineColor Color
	BlockquoteTextColor Color
	BlockquoteFont      string

	// Horizontal rule
	HRColor     Color
	HRLineWidth float64

	// Output
	DPI         int
	Trim        bool
	TrimPadding float64
}

Config holds all customizable rendering options.

func DefaultConfig added in v1.1.0

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

Directories

Path Synopsis
cmd
md2img command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL