Skip to content

Template Engine: Pandoc

Joe Snee edited this page Sep 9, 2019 · 5 revisions

Using Pandoc as a templating engine is deprecated, but still supported. In order to use Pandoc as your templating engine, you'll first need to install Pandoc version 2.3 or greater. While it is officially supported, features introduced in the future may cause templating with Pandoc to break occasionally. If this happens, please log a bug through GitHub.

More information on how to format the data used in component files can be found in Pandoc's Metadata Block documentation. (Despite only having examples for .yaml metadata, Pandoc's documentation does mention it supports .json data, as well)

NOTE:

While there will continue to be support for templating using Pandoc, DMBinder is switching to Handlebars as the primary means of templating. This can be changed in the extension settings by changing dmbinder.defaultTemplatingEngine. This switch is for ease of use (no outside installations necessary) and due to some shortcomings of Pandoc's templating system.

Spell Block Template (Pandoc)

Example:

#### $name$
**Source** $source$
**School** $school$; **Level** $for(classes)$$classes.name$ $classes.level$$sep$, $endfor$
___
- **Casting Time** $castTime$
- **Components** $for(components)$$components$$sep$, $endfor$
- **Range** $range$ $if(area)$($area$)$endif$
- **Effect** $effect$
- **Duration** $duration$
$if(savingThrow)$- **Saving Throw** $savingThrow$$endif$
$if(resistance)$- **Spell Resistance** $resistance$$endif$

$description$

Despite looking somewhat messy, Pandoc's templating system was implemented over using VS Code or TextMate "snippets", due to their benefits, particularly regarding the handling of lists and conditional logic. Template files should look just like regular Markdown (.md) files, but with specially formatted placeholders that will be replaced with the data from a component. This allows, for example, all the descriptive blocks (spells, items, monsters, NPCs, magic shops, cities, etc) in your campaign documents to have a similar and consistent layout. Gone are the days where the order of monster stats changed from monster to monster!

There are 3 main features of Pandoc's templating system:

  • Variables: $variableName$
  • Conditions: $if(variableName)$Render if variable has value: $variableName$. Cool, right?$endif$
  • Loops: $for(listVariable)$Each value: $listVariable$$sep$, $endfor$

Variables

Variables are accessed based on the names defined in the component files and nested variables are accessed using the . character to denote a nested attribute.

Example:
Component
name: Cool Dude
equipment:
  weapon: Greatsword
  armor: Plate Mail
Template
**Name:** $name$
$name$ wields a *$equipment.weapon$* and is protected by their hardy *$equipment.armor$*.
Output
**Name:** Cool Dude
Cool Dude wields a *Greatsword* and is protected by their hardy *Plate Mail*.

Conditions

Conditions can control if content listed between the opening statement and the closing statement are output, based on checking if a variable exists.

Example:
Component
name: Cool Dude
equipment:
  meleeWeapon: Greatsword
  armor: Plate Mail
Template
**Name:** $name$
$name$ wields a *$equipment.weapon$*$if(equipment.armor)$ and is protected by their hardy *$equipment.armor$*$endif$.
$if(equipment.rangedWeapon)$$name$ also is pretty handy with their $equipment.rangedWeapon$, too!$endif$
Output
**Name:** Cool Dude
Cool Dude wields a *Greatsword* and is protected by their hardy *Plate Mail*.
 

The part about the armor is output because equipment.armor has a value, but the next line is blank because there is no equipment.rangedWeapon defined in the component metadata.

Important Note: Notice that in the example provided, there is a blank line displayed at the end, because there is a new line before the $if(equipment.rangedWeapon)$. In order to not see that empty line, you would need to start the $if()$ statement at the end of the previous line like so:

**Name:** $name$
$name$ wields a *$equipment.weapon$*$if(equipment.armor)$ and is protected by their hardy *$equipment.armor$*$endif$.$if(equipment.rangedWeapon)$
$name$ also is pretty handy with their $equipment.rangedWeapon$, too!$endif$

This is why template files can start to look incredibly messy using Pandoc, but the benefits can outweigh the clutter.

Loops

Loops are a great way to format a list of data!

Loops Separator

Another nifty feature of Pandoc is that you can define a separator for loops. The separator is optional and is specified at the very end of the loop. If present, anything put between $sep$ and the $endfor$ will be added between every item in the list. For instance, if you wanted a list to generate a comma separated list you could do something like this:

Example:

Component:

name: Cool Dude
inventory:
  - a Bag of Holding
  - a bedroll
  - rations (x7)
  - 7 gp

Template:

**Name:** $name$
$name$ is holding $for(inventory)$$inventory$$sep$, $endfor$.

Output:

**Name:** Cool Dude
Cool Dude is holding a Bag of Holding, a bedroll, rations (x7), 7 gp.
Example:
Component
name: Cool Dude
equipment:
  weapon: Greatsword
  armor: Plate Mail
inventory:
  - Bag of Holding
  - Bedroll
  - Rations (x7)
  - 7 gp
saleItems:
  - name: +1 *Ring of Protection*
    cost: 2,000 gp
  - name: Masterwork Crossbow
    cost: 100 gp
Template
**Name:** $name$
$name$ wields a *$equipment.weapon$* and is protected by their hardy *$equipment.armor$*.
**Inventory:**
$for(inventory)$
- $inventory$
$endfor$
**Items For Sale:**
| Name | Cost |
|:----:|:----:|
$for(saleItems)$
| $saleItems.name$ | $saleItems.cost$ |
$endfor$
Output
**Name:** Cool Dude
Cool Dude wields a *Greatsword* and is protected by their hardy *Plate Mail*.
**Inventory:**
- Bag of Holding
- Bedroll
- Rations (x7)
- 7 gp
**Items For Sale:**
| Name | Cost |
|:----:|:----:|
| +1 <em>Ring of Protection</em> | 2,000 gp |
| Masterwork Crossbow | 100 gp |