Skip to content

plbarrio/draft-mark

Repository files navigation

draft-mark.lua

test License: GPL v3 Pandoc Quarto

Pandoc Lua filter that adds a visible draft mark to document output. Useful for editorial workflows where you need to track revision status per document and see it in printed or PDF output.

Copyright 2026 Pedro Luis Barrio under GPL-3.0-or-later, see LICENSE file for details.

Maintained by plbarrio.

Requirements

Pandoc >= 2.19.1 · Quarto >= 1.4.0 (for Quarto usage)

Typst output needs Pandoc >= 3.1.3 (Pandoc's Typst writer didn't exist before that) — CI tests against 3.1.3 and latest for this reason, even though the filter's own Lua code only requires 2.19.1.

Usage

Plain Pandoc

pandoc --lua-filter=draft-mark.lua input.md -o output.pdf

Quarto

Declare in _quarto.yml:

filters:
  - draft-mark.lua

Or install as an extension:

quarto add plbarrio/draft-mark

How it works

Each document carries its own mark string in the frontmatter. A global flag controls whether marks are visible in the output. This separation allows you to:

  • Keep the mark in the document at all times (no renaming, no git noise)
  • Show marks when compiling drafts for review
  • Hide marks for final production output — with no changes to the source files

The mark is inserted right after the first Header in the document (or at the top, if the document has none).

Configuration

Per-document mark (frontmatter)

draft-mark: "rc1"

Any string is valid: draft, rc1, rev-joan, 1.0.0, borrador, etc. If absent, no mark is emitted even when the global flag is on.

Global show flag

draft-mark-show: true

Set this in the master YAML, a defaults file, or passed via --metadata-file (or -M draft-mark-show=true on the command line — it's a top-level key, so it works fine with -M). When false or absent, no marks are emitted.

The two keys are independent — they live in different files and do not conflict. This is intentional: draft-mark belongs to each document, draft-mark-show belongs to the build configuration.

Typical workflow

000_book.yaml          ← draft-mark-show: true  (review builds)
000_book_final.yaml    ← draft-mark-show: false  (production)

101_poem.md            ← draft-mark: "draft"
102_poem.md            ← draft-mark: "rc1"
103_poem.md            ← draft-mark: "rev-joan"
104_poem.md            ← (no mark — considered done)

Review build:

pandoc --metadata-file=000_book.yaml --lua-filter=draft-mark.lua ...

Production build:

pandoc --metadata-file=000_book_final.yaml --lua-filter=draft-mark.lua ...

Format output

The mark is inserted after the first heading, in a subdued style (gray, small text), as a margin note for LaTeX/ConTeXt/Typst so it has no layout impact.

Format Output
html/epub <div class="draftmark" data-mark="rc1"><p>rc1</p></div>
latex \marginpar{\color{gray}\ttfamily\small rc1}
context \draftmark{rc1}
typst #place(right, dy: -1em, text(size: 0.8em, fill: gray, [rc1]))

CSS (HTML/EPUB)

.draftmark {
  text-align: right;
  font-size: 0.75em;
  color: #999;
  font-family: monospace;
  margin-bottom: 0.5em;
}

LaTeX

Requires \usepackage{color} or \usepackage{xcolor} in the preamble.

\usepackage{xcolor}

ConTeXt

\draftmark{...} is a plain macro call — define it with \definehighlight (or any command taking one argument) in your ConTeXt style file:

\definehighlight[draftmark][style=\tfx, color=gray]

Tests

make test           # run all tests (plain pandoc)
make test-quarto    # run all tests via `quarto pandoc`
make generate-tests # regenerate expected output
File What it covers
draft-mark-on.md mark visible after the first Header when show is true
draft-mark-off.md mark hidden when show is absent
draft-mark-none.md show is true but no mark defined — nothing emitted
draft-mark-existing.md a Div.draftmark already present (as left by a collection.lua per-source pass) — insertion is skipped, the existing mark is still rendered

Design notes

Internally the filter works in two stages so it also behaves correctly when run per-source inside a collection.lua build (where sources are processed with FORMAT=json before the final output format is known):

  1. Insertion (Pandoc pass) always builds a generic Div.draftmark and places it after the first Header, regardless of FORMAT. A Div is a plain Pandoc block, so it survives a json/native round-trip untouched. If the document already contains a Div.draftmark (e.g. because this is an already-merged collection build where each source got its own mark), insertion is skipped — see draft-mark-existing.md above.
  2. Rendering (Div pass) converts Div.draftmark into the format-native markup (\marginpar{}, \draftmark{}, #place(...)), but only once FORMAT is a real output format. It no-ops for json/native, so it's safe to also run at the per-source stage.

This mirrors the class-whitelist/environment mechanism used by the sibling container-writer.lua filter (wrap a Div/Span by class into format-native markup) — but container-writer only wraps elements that already exist in the AST with a known class; it doesn't read metadata, decide whether to show anything, pick the mark text, or find where to insert it. Those three things (draft-mark-show, draft-mark, and locating the first Header) are this filter's actual job, so it keeps its own rendering step instead of depending on container-writer being present. If your build already loads container-writer.lua, you can replace the rendering step with a draftmark whitelist entry there instead — but that trades a few lines of Lua for a hard dependency, so it's left as a standalone default here.

A book-wide (as opposed to per-document) mark is better handled outside this filter entirely: reference the plain $draft-mark$ metadata field directly in a title-page template (e.g. via frontmatter.lua), with the format-specific wrapping written into the template itself.

Known limitations

  • Only the first Header is used as the anchor point — the mark is not repeated at every heading level.
  • container-writer's environment mechanism could in principle wrap Div.draftmark too (see Design notes) — but that's a deliberate trade-off, not a limitation to fix.

Issues and contributing

Issues and PRs welcome at the project repository.

License

GPL-3.0-or-later. See LICENSE.

References

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors