Skip to content

Repository files navigation

frontmatter.lua

Pandoc Lua filter that inserts front and back matter pages from format-specific template files. Template files can reference any YAML metadata field using $field$ syntax.

Works standalone or combined with dc-mapper.lua (run dc-mapper first).

Problem

Pandoc's --include-before-body (-B) and --include-in-header options insert raw file content without expanding any variables (issue #812, open since 2014). A copyright page or dedication included with -B cannot use $title$, $author$, or any other metadata field — it arrives as literal text.

The canonical workaround suggested by jgm is a two-pass workflow: the snippet is first processed as a Pandoc template to expand variables, and the resulting output is then included in the main document:

pandoc -H <(pandoc --template=copyright.tex input.md) input.md -o output.pdf

This approach leverages the full Pandoc template engine, including conditionals and loops ($if(field)$, $for(list)$, etc.), making it the most expressive solution for complex front and back matter.

However, it requires shell-specific features such as process substitution or temporary files, which reduces portability in environments like Windows, editors, or CI systems.

This filter provides a lightweight, single-pass alternative for simple metadata substitution. It avoids shell dependencies by performing minimal variable expansion ($field$ only) directly in Lua, at the cost of not supporting template logic such as conditionals or loops.

Requirements

Pandoc >= 2.19.1

Usage

# standalone
pandoc --lua-filter=frontmatter.lua input.md -o output.pdf
# combined with dc-mapper
pandoc --lua-filter=dc-mapper.lua \
--lua-filter=frontmatter.lua \
input.md -o output.pdf

YAML

frontmatter:
- templates/dedication
- templates/copyrightbackmatter:
- templates/colophon

frontmatter and backmatter accept a single string or a list:

frontmatter: templates/dedication

Each entry is a path without extension. The format extension is appended automatically.

Supported formats

FormatExtension
latex.tex
context.ctx
typst.typ
html.html

Not supported: docx (Pandoc's docx writer does not support raw fragment insertion).

Templates for unsupported formats are skipped with a warning to stderr.

Template variables

Any YAML metadata field is available as $field$:

$title$, $author$, $date$

Nested fields use dot notation:

copyright:
- owner: Jane Doeyear: 2026
$copyright.owner$, $copyright.year$

For lists of maps, the shortcut without index joins all values with , , and all items are accessible by index:

creator:
- role: authortext: Jane Doe
- role: editortext: John Smith
$creator.text$ → "Jane Doe, John Smith" ← all values joined with ', '
$creator.1.text$ → "Jane Doe"
$creator.2.text$ → "John Smith"

If you need multiple structured identifiers (e.g. ISBN and DOI), run dc-mapper.lua first — it maps them to flat variables $isbn$ and $doi$ before frontmatter.lua runs.

Scalar lists are joined with , :

author:
- Jane Doe
- John Smith
$author$ → "Jane Doe, John Smith"

Special characters: values are rendered through the Pandoc writer, so characters with special meaning in the output format are escaped automatically. For example, 50% in a LaTeX template becomes 50\% (valid LaTeX that renders as 50% in the PDF).

Missing variables: if a placeholder has no matching variable it is removed from the output and a warning is emitted to stderr:

[frontmatter] warning: variable not found: "$isbn$" in "templates/copyright.tex"

Note: substitution is not recursive — a substituted value containing $key$ will not be expanded further.

Inline formatting in metadata fields (e.g. dedication: "A mi *querida* familia") is rendered to format-native markup — \emph{} in LaTeX, <em> in HTML. Block-level fields (multi-paragraph abstract, etc.) are also rendered natively, preserving paragraph breaks and inline formatting. Typographic characters (smart quotes, em-dashes) are preserved in both cases.

Math and special syntax: placeholder cleanup only matches $identifier$ patterns (starting with a letter or _, followed by alphanumerics, ., or _). Math expressions such as $E = mc^2$ or Typst math are left untouched.

When combined with dc-mapper.lua, normalized variables like $author$, $title$, $isbn$, $abstract$ are available directly.

Template search path

Templates are searched in PANDOC_STATE.resource_path (default: .). Extend via --resource-path or defaults.yaml:

pandoc --resource-path=.:templates --lua-filter=frontmatter.lua ...
resource-path:
- .
- /path/to/shared/templates

The first matching file is used. If not found, a warning lists the directories searched:

[frontmatter] warning: template not found: "templates/dedication.tex"
(searched: ., /path/to/shared/templates)

Warnings

MessageMeaning
[frontmatter] warning: variable not found: "$X$" in "Y"Placeholder $X$ has no matching variable — removed from output.
[frontmatter] warning: template not found: "X" (searched: Y)Template file not found in any resource-path directory.
[frontmatter] warning: unsupported format "X", skipping "Y"Output format has no template extension mapping.

Example

title: The Art of Typesettingcopyright:
- owner: Jane Doeyear: 2026publisher: Editorial Examplerights: "© 2026 Jane Doe. All rights reserved."frontmatter:
- templates/dedication
- templates/copyrightbackmatter:
- templates/colophon

templates/copyright.tex:

\begin{flushleft}
\textit{$title$} \\[0.5em]
Copyright \copyright\ $copyright.year$$copyright.owner$\\[0.5em]
$publisher$$rights$\end{flushleft}
\clearpage

templates/copyright.html:

<divclass="copyright"><p><em>$title$</em></p><p>Copyright &copy; $copyright.year$ $copyright.owner$</p><p>$publisher$</p><p>$rights$</p></div>

Tests

make test# run all tests
make generate-tests # regenerate expected output from current filter
FileWhat it covers
basic.mdfrontmatter + backmatter insertion in latex
single-string.mdfrontmatter and backmatter as a single string
nested.mdnested map access ($copyright.owner$, $copyright.year$)
multi-author.mdscalar list joined with ', '
missing-template.mdwarning emitted to stderr when template not found
utf8.mdUTF-8 content in template variables
percent.md% character in variable values
html.mdhtml format — uses .html template extension
math.mdmath expressions in templates ($E = mc^2$) are not eaten by placeholder cleanup
indexed-list.mdindexed list access ($creator.1.text$, $creator.2.text$) and shortcut join ($creator.text$ = all values joined)
multi-map.mdshortcut joins all values when multiple items share the same field key ($creator.text$ = "Jane, John")
blocks.mdblock-level metadata rendered natively (paragraph breaks and bold preserved)

Related

dc-mapper.lua — maps Dublin Core / EPUB structured metadata to simple Pandoc variables. When used together, run dc-mapper.lua first so that variables like $title$, $author$, $isbn$ are available to frontmatter.lua templates.

About

A Pandoc & Quarto Lua filter to automatically inject dynamic frontmatter and backmatter templates.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages