declaring arbitrary variables for pandoc conversion using YAML metadata block

2k Views Asked by At

I've only recently discovered Pandoc, so I'm still getting to used to it a lot of its features. It looks like an incredibly useful tool and I'm excited to find out some applications for it. I've been consulting the User's Guide, and while there is a section on what I'd like to know, I can't seem to get the desired output. I'm not sure if I'm reading the entry correctly.

Put simply, I have a document in .markdown which acts as a template. From this template, I'd like to produce several other documents (probably .odt and .docx). These documents will be mostly identical, apart from a few pieces of information which I'd like to change. What I'd like to know is, is it possible to change these pieces of information by declaring a variable in the YAML metadata at the top of document.

For example, say I had the following in my .markdown template:

---
key-one: "value-one"
key-two: "value-two"
key-three: "value-three"
---
# DocumentTitle
## DocumentSubtitle

This line contains the first value, called $key-one$

This line contains the second value, called $key-two$

This line contains the third value, called $key-three$

Is there a way that I can get pandoc to replace the 'placeholders' i.e. key-one, key-two, etc., with the information that is declared in the YAML metadata? This would result in:

This line contains the first value, called value-one
This line contains the second value, called value-two
This line contains the third value, called value-three

On the User's Guide, it says:

If a variable is not set, pandoc will look for the key in the document’s metadata – which can be set using either YAML metadata blocks or with the --metadata option.

From what I can find on the User's Guide, it seems that I can't declare values arbitrarily in the metadata. I have found some information about Pandoc templates, but I'm not sure how I would have to edit these (or create a custom one) to get the desired output.

Please let me know if anything isn't clear and I will try to be more specific. Thanks in advance.

2

There are 2 best solutions below

5
On BEST ANSWER

Pandoc templates only contain the header/footer etc. around the document body text, which gets placed where you see $body$ in the template. So templates cannot be used to substitute variables in the document body.

For that, you could use this pandoc filter, save it to say meta-vars.lua:

local vars = {}

function get_vars (meta)
  for k, v in pairs(meta) do
    if v.t == 'MetaInlines' then
      vars["$" .. k .. "$"] = {table.unpack(v)}
    end
  end
end

function replace (el)
  if vars[el.text] then
    return pandoc.Span(vars[el.text])
  else
    return el
  end
end

return {{Meta = get_vars}, {Str = replace}}

And call it with pandoc input.md --lua-filter meta-vars.lua

0
On

I'd like to offer an adjustment to the above lua filter from @mb21. My adjustments fix the indue of needing to have whitespace characters on either side of your variable sin the text and allow you to substitute multiple word for a single variable.

local vars = {}

function get_vars (meta)
  for k, v in pairs(meta) do
    if pandoc.utils.type(v) == 'Inlines' then
      vars["{{" .. k .. "}}"] = {table.unpack(v)}
    end
  end
end

function replace (el)
  for k, v in pairs(vars) do

    local startIndex, endIndex = string.find(el.text, k) 
    if startIndex then

      local preceding = string.sub(el.text, 1, startIndex - 1)
      if string.len(preceding) > 0 then
        table.insert(v, 1, preceding)
      end

      local remaining = string.sub(el.text, endIndex+1)
      if string.len(remaining) > 0 then
        table.insert(v, remaining)
      end

      return v
    end
  end
  return nil
end


return {{Meta = get_vars}, {Str = replace}}