The TeXnative extension lives in _extensions/texnative/ with this structure:
_extensions/texnative/
├── _extension.yml # Extension configuration (filters, partials, defaults)
├── texnative.lua # Main Lua filter
├── texnative_core.lua # Shared Lua utilities (require'd by other Lua files)
├── texnative_dcl.lua # Data classification label filter
├── texnative-pre.lua # Pre-processing filter
├── pandoc.tex # Main template partial (calls other partials)
├── header.tex # Custom header includes (currently empty)
├── before-body.tex # Content before document body
└── partials/ # LaTeX partial templates
├── document-colors.tex
├── document-background.tex
├── document-header-footer.tex
├── document-text-style.tex
├── page-cover.tex
├── page-title.tex
└── toc.tex
Quarto's template-partials in _extension.yml only override existing Pandoc partial names. Custom partial names (like document-colors.tex) are NOT automatically included in the LaTeX output.
pandoc.texis a standard Pandoc partial name that gets processed- Our
pandoc.texexplicitly calls custom partials using$partial-name.tex()$syntax:$document-colors.tex()$ $document-background.tex()$ $document-header-footer.tex()$
- These custom partials must be listed in
template-partialsin_extension.yml
Option 1: Via Existing Partial (Recommended for simple additions)
Add your LaTeX code to an existing partial like document-colors.tex or document-header-footer.tex.
Option 2: Via Lua Filter + header-includes (Recommended for dynamic content)
Generate LaTeX in a Lua filter and inject it into header-includes:
function Meta(meta)
local latex_code = "\\usepackage{mypackage}\n..."
local raw_block = pandoc.RawBlock('latex', latex_code)
if not meta['header-includes'] then
meta['header-includes'] = pandoc.MetaList({})
end
meta['header-includes']:insert(pandoc.MetaBlocks({raw_block}))
return meta
endThis approach:
- Works reliably (bypasses template-partial complexity)
- Allows conditional logic in Lua
- Can use metadata values to generate dynamic LaTeX
Option 3: New Partial (More complex)
- Create
partials/my-partial.tex - Add to
template-partialslist in_extension.yml - Add
$my-partial.tex()$call inpandoc.tex
-
Partial not appearing in output: Check that
pandoc.texcalls your partial AND it's intemplate-partials -
Template variables not resolved:
include-in-headerfiles are included as raw LaTeX - they do NOT process Pandoc template syntax like$if(var)$. Use partials or Luaheader-includesinjection instead. -
Lua module naming: Use underscores (
texnative_dcl.lua) not hyphens for Lua files that need to berequire()d in tests. -
Filter return value: Lua filters must return
{ {Meta = Meta} }format for Quarto, but can return a module tableMfor unit testing:if quarto then return { { Meta = Meta } } else return M -- For unit testing end
Tests live in tests/unit/ using the Busted framework:
- Mock Pandoc via
tests/mocks/pandoc.lua - Run with:
busted tests/unit/ - Pattern:
*_spec.lua
Test setup pattern:
package.path = 'tests/mocks/?.lua;' .. package.path
_G.pandoc = require('pandoc')
package.path = '_extensions/texnative/?.lua;' .. package.path
local module = require('module_name')