Picoloom
This is a small, opinionated Go library and CLI for Markdown to PDF. It handles cover pages, tables of contents, watermarks, signatures, and more. You can use CSS themes and custom assets. It works with parallel batch processing. The engine behind it is Chrome. It does not rely on LaTeX.
Install / Use
/learn @alnah/PicoloomREADME
Picoloom
If you need something between the complexity of Pandoc and the speed of Markdown-to-PDF converters with limited styling options, Picoloom might be a good fit. It is a small, opinionated Go library and CLI for converting Markdown to PDF that I created to share teaching materials with my French students. I designed it to be easy enough, fast enough, and polished enough for that purpose. It supports cover pages, tables of contents, watermarks, signatures, and more. You can also use CSS themes and custom assets. It supports parallel batch processing. Under the hood, it uses Chrome, and it does not rely on LaTeX. I hope it will be useful to you as well.

Table of Contents
- Installation
- Quick Start
- Features
- CLI Reference
- Environment Variables
- Configuration
- Config Init Wizard
- Library Usage
- Troubleshooting
- Known Limitations
- Contributing
Installation
go install github.com/alnah/picoloom/v2/cmd/picoloom@latest
The current Go module path is github.com/alnah/picoloom/v2.
Homebrew
brew tap alnah/tap
brew install alnah/tap/picoloom
Update later with:
brew upgrade alnah/tap/picoloom
On a fresh machine without Chrome installed yet, picoloom doctor stays strict by
default. Use picoloom doctor --allow-managed-browser to validate the managed
Chromium bootstrap path used on first run.
Docker
docker pull ghcr.io/alnah/picoloom:latest
Binary Download
Download pre-built binaries from GitHub Releases.
</details>Requirements
- Go 1.25+
- Chrome/Chromium (downloaded automatically on first run)
- Homebrew users can install the CLI from
alnah/tap/picoloom
Docker/CI users: See Troubleshooting for setup instructions.
Quick Start
CLI
picoloom convert document.md # Single file
picoloom convert ./docs/ -o ./output/ # Batch convert
picoloom convert -c work document.md # With config
picoloom config init # Create config with wizard
Library
package main
import (
"context"
"log"
"os"
"github.com/alnah/picoloom/v2"
)
func main() {
conv, err := picoloom.NewConverter()
if err != nil {
log.Fatal(err)
}
defer conv.Close()
result, err := conv.Convert(context.Background(), picoloom.Input{
Markdown: "# Hello World\n\nGenerated with Picoloom.",
})
if err != nil {
log.Fatal(err)
}
os.WriteFile("output.pdf", result.PDF, 0644)
}
The Convert() method returns a ConvertResult containing:
result.PDF- the generated PDF bytesresult.HTML- the intermediate HTML (useful for debugging)
Use Input.HTMLOnly: true to skip PDF generation and only produce HTML.
Features
- CLI + Library - Use as
picoloomcommand or import in Go, with shell completion - Batch conversion - Process directories with parallel workers
- Cover pages - Title, subtitle, logo, author, organization, date, version
- Table of contents - Auto-generated from headings with configurable depth
- Frontmatter stripping - YAML frontmatter (
---blocks) stripped before conversion - Custom styling - Embedded themes or your own CSS (some limitations)
- Page settings - Size (letter, A4, legal), orientation, margins
- Signatures - Name, title, email, photo, links
- Footers - Page numbers, dates, status text
- Watermarks - Diagonal background text (BRAND, etc.)
CLI Reference
picoloom convert document.md # Single file
picoloom convert ./docs/ -o ./output/ # Batch convert
picoloom convert -c work document.md # With config
picoloom convert --style technical doc.md # With style
picoloom config init # Interactive config wizard
<details>
<summary>All flags</summary>
picoloom <command> [flags] [args]
Commands:
convert Convert markdown files to PDF
config Manage configuration files
doctor Check system configuration
completion Generate shell completion script
version Show version information
help Show help for a command
picoloom convert <input> [flags]
Input/Output:
-o, --output <path> Output file or directory
-c, --config <name> Config file name or path
-w, --workers <n> Parallel workers (0 = auto)
-t, --timeout <duration> PDF generation timeout (default: 30s)
Examples: 30s, 2m, 1m30s
Author:
--author-name <s> Author name
--author-title <s> Author professional title
--author-email <s> Author email
--author-org <s> Organization name
--author-phone <s> Author phone number
--author-address <s> Author postal address
--author-dept <s> Author department
Document:
--doc-title <s> Document title ("" = auto from H1)
--doc-subtitle <s> Document subtitle
--doc-version <s> Version string
--doc-date <s> Date (see Date Formats section)
--doc-client <s> Client name
--doc-project <s> Project name
--doc-type <s> Document type
--doc-id <s> Document ID/reference
--doc-desc <s> Document description
Page:
-p, --page-size <s> letter, a4, legal (default: letter)
--orientation <s> portrait, landscape (default: portrait)
--margin <f> Margin in inches (default: 0.5)
Footer:
--footer-position <s> left, center, right (default: right)
--footer-text <s> Custom footer text
--footer-page-number Show page numbers
--footer-doc-id Show document ID in footer
--no-footer Disable footer
Cover:
--cover-logo <path> Logo path or URL
--cover-dept Show author department on cover
--no-cover Disable cover page
Signature:
--sig-image <path> Signature image path
--no-signature Disable signature block
Table of Contents:
--toc-title <s> TOC heading text
--toc-min-depth <n> Min heading depth (1-6, default: 2)
1=H1, 2=H2, etc. Use 2 to skip title
--toc-max-depth <n> Max heading depth (1-6, default: 3)
--no-toc Disable table of contents
Watermark:
--wm-text <s> Watermark text
--wm-color <s> Color hex (default: #888888)
--wm-opacity <f> Opacity 0.0-1.0 (default: 0.1)
--wm-angle <f> Angle in degrees (default: -45)
--no-watermark Disable watermark
Page Breaks:
--break-before <s> Break before headings: h1,h2,h3
--orphans <n> Min lines at page bottom (default: 2)
--widows <n> Min lines at page top (default: 2)
--no-page-breaks Disable page break features
Assets & Styling:
--style <name|path> CSS style name or file path (default: default)
Name: uses embedded or custom asset (e.g., "technical")
Path: reads file directly (contains / or \)
--template <name|path> Template set name or directory path
--asset-path <dir> Custom asset directory (overrides config)
--no-style Disable CSS styling
Debug Output:
--html Output HTML alongside PDF
--html-only Output HTML only, skip PDF generation
Output Control:
-q, --quiet Only show errors
-v, --verbose Show detailed timing
picoloom config init [flags]
Config Init:
--output <path> Output path for generated config (default: ./picoloom.yaml)
--force Overwrite destination if it exists
--no-input Use defaults without interactive prompts
</details>
<details>
<summary>Examples</summary>
# Single file with custom output
picoloom convert -o report.pdf input.md
# Batch with config
picoloom convert -c work ./docs/ -o ./pdfs/
# Custom CSS, no footer
picoloom convert --style ./custom.css --no-footer document.md
# A4 landscape with 1-inch margins
picoloom convert -p a4 --orientation landscape --margin 1.0 document.md
# With watermark
picoloom convert --wm-text "DRAFT" --wm-opacity 0.15 document.md
# Override document title
picoloom convert --doc-title "Final Report" document.md
# Page breaks before H1 and H2 headings
picoloom convert --break-before h1,h2 document.md
# Use embedded style by name
picoloom convert --style technical document.md
# Debug: output HTML alongside PDF
picoloom convert --html document.md
# Debug: output HTML only (no PDF)
picoloom convert --html-only document.md
# Use custom assets directory
picoloom convert --asset-path ./my-assets document.md
# Interactive config wizard
picoloom config init
# Non-interactive config generation (CI/scripts)
picoloom config init --no-input --output ./configs/work.yaml --force
