glu — Markdown, HTML & Lua to PDF
glu converts Markdown and HTML files to PDF, or executes Lua scripts for full programmatic control over PDF output.
glu document.md # → document.pdf
glu page.html # → page.pdf
glu script.lua # → script.pdfInstallation
Homebrew (macOS / Linux):
brew install boxesandglue/tap/gluPre-built binaries:
Download the latest release from https://github.com/boxesandglue/glu/releases/latest.
Build from source (requires Go):
git clone https://github.com/boxesandglue/glu
cd glu
rake buildQuick start
Scaffold a starter project and build it:
glu init mydoc
cd mydoc
glu index.mdThis produces index.pdf — an A4 page with title block, page numbers via CSS, and a small tour of Markdown features (headings, lists, table, inline Lua, links). The default report template ships inside the glu binary, so there’s nothing to download or configure.
Two other templates round out the staged-difficulty arc: bare (just index.md, minimal Markdown) and pdfua (preconfigured for format: PDF/UA with structural elements). Pass the name as a second argument:
glu init mydoc bare
glu init mydoc pdfua
glu init list # show available templatesSee Using glu for the full grammar and overwrite rules. You can also skip the scaffolder entirely and pass any .md, .html, or .lua file directly — every file the templates emit is valid standalone input.
Dynamic content with Lua
glu supports embedded Lua for calculations, data processing, and dynamic content. If a file hello.lua exists alongside hello.md, it is loaded automatically:
hello.lua:
function greeting(name)
return "Hello, " .. name .. "!"
endhello.md:
# Welcome
{= greeting("World") =}The {= expr =} syntax inserts the result of any Lua expression. See Markdown mode for the full feature set including Lua code blocks, YAML frontmatter, and custom CSS.
Callbacks
glu provides a callback system for page-level and element-level events. For example, drawing a frame on every page or decorating headings:
local frontend = require("glu.frontend")
-- Runs when a new page is created (for backgrounds)
frontend.add_callback("page_init", "bg", function(doc, page, pagenum, pageinfo)
-- draw backgrounds, watermarks, etc.
end)
-- Runs before each page is written to the PDF (for overlays)
frontend.add_callback("pre_shipout", "frame", function(doc, page, pagenum, pageinfo)
-- draw frames, add page numbers, etc.
end)
-- Runs after each block element (h1, p, ul, ...) is built
frontend.add_callback("post_element", "highlight", function(element, doc)
-- collect headings, add backgrounds, etc.
end)Named callbacks can be ordered and removed dynamically. See Callbacks for details.
Lua script mode
For full programmatic control, pass a .lua file. This hello world example creates a PDF with a formatted paragraph:
local frontend = require("glu.frontend")
-- Load a font
local doc = frontend.new("hello.pdf")
local ff = doc:new_font_family("text")
local fs = frontend.fontsource({ location = "fonts/CrimsonPro-Regular.ttf" })
ff:add_member(fs, "regular", "normal")
-- Create text content
local txt = frontend.text({
font_family = ff,
font_size = "12pt",
color = "black"
})
local str = [[The quick brown fox jumps over the lazy dog with a very
long line that should be wrapped at some point. This is a test to see
how the text is formatted when it is too long to fit on one line.]]
-- Remove newlines
str = str:gsub("\n", " ")
txt:append(str)
-- Format paragraph and create page
local vlist = doc:format_paragraph(txt, "225pt", { leading = "14pt" })
local page = doc:new_page()
page.width = "210mm"
page.height = "297mm"
page:output_at("1cm", "28cm", vlist)
page:shipout()
doc:finish()
print("Created hello.pdf")See Lua script mode for a step-by-step explanation and all available modules.
Documentation
- Markdown mode — Markdown to PDF conversion (frontmatter, Lua blocks, CSS)
- HTML mode — HTML to PDF conversion (full CSS,
<style>tags) - Callbacks — lifecycle and page/element event callbacks
- Lua script mode — programmatic PDF creation with full Lua API
- Using glu — command line options and general usage
- glu.frontend module — Lua typesetting API (documents, text, fonts, tables)
- glu.json module — JSON encoding, decoding, file I/O
- glu.log module — structured logging (debug, info, warn, error)
- glu.pdf module — low-level PDF writing
- xml.cxpath module — XPath XML querying
- hobby module — MetaPost-style path drawing (Hobby curves, transformations, SVG output)
- Backend modules — glu, glu.node, glu.font
- glu.textshape module — low-level text shaping (HarfBuzz port)