Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pandoc lua filter: How to preserve footnotes' formatting?

I try to implement footnotes as defined in CSS Generated Content for Paged Media Module.
With this definition, footnotes have to be inline spans. I wrote a first draft of a pandoc lua filter.
It is my first pandoc filter (and also the first time I code in lua).

Here's the filter:

Note = function (elem)
  local textContent = {}
  local content = elem.content
  for i = 1, #content do
    textContent[2*i-1] = pandoc.Str(pandoc.utils.stringify(content[i]))
    if i < #content
    then
      textContent[2*i] = pandoc.LineBreak()
    end
  end
  return pandoc.Span(textContent, pandoc.Attr("", {"footnote"}, {}))
end

It works well for footnotes with unformatted text (formatting is lost due to the use of the stringify() function): simple footnotes and multiple blocks footnotes are well rendered.

In order to preserve formatting, I tried to use the walk_block() function on the content of the Note element, but I cannot obtain any result.

I got a second problem: the stringify() function returns a void character string for CodeBlock elements.

So, when I use this filter on the following markdown text:

Here is a footnote reference,[^1] and another.[^longnote]

[^1]: Here is the footnote.

[^longnote]: Here's one with multiple blocks.

    Subsequent paragraphs are indented to show that they
belong to the previous footnote.

        { some.code }

    The whole paragraph can be indented, or just the first
    line.  In this way, multi-paragraph footnotes work like
    multi-paragraph list items.

This paragraph won't be part of the note, because it
isn't indented.

I obtain the following HTML fragment:

<p>
  Here is a footnote reference,
  <span class="footnote">Here is the footnote.</span>
  and another.
  <span class="footnote">Here’s one with multiple blocks.
    <br />
    Subsequent paragraphs are indented to show that they belong to the previous footnote.
    <br />
    <br />
    The whole paragraph can be indented, or just the first line. In this way, multi-paragraph footnotes work like multi-paragraph list items.
  </span>
</p>
<p>This paragraph won’t be part of the note, because it isn’t indented.</p>

The code block is lost. Is there any way to keep both footnotes' formatting and code blocks?

like image 953
RLesur Avatar asked Jul 26 '18 23:07

RLesur


1 Answers

I found how to process the Note element.

First, the Note element is an inline element, so we can use walk_inline. The weird thing is that a Note element can embed block elements like Para or CodeBlock.

The following filter deal only with Para and CodeBlock elements. Formatting is kept.

Since a Para element is a list of inline elements, it is obvious to reuse those elements in a Span element.
The CodeBlock text can also be processed in an inline Code element.

local List = require 'pandoc.List'

Note = function (elem)
  local inlineElems = List:new{} -- where we store all Inline elements of the footnote
  -- Note is an inline element, so we have to use walk_inline
  pandoc.walk_inline(elem, {
    -- Para is a list of Inline elements, so we can concatenate to inlineElems
    Para = function(el)
      inlineElems:extend(el.content)
      inlineElems:extend(List:new{pandoc.LineBreak()})
    end,
    -- CodeBlock is a block element. We have to store its text content in an inline Code element
    CodeBlock = function(el)
      inlineElems:extend(List:new{pandoc.Code(el.text, el.attr), pandoc.LineBreak()})
    end
  })
  table.remove(inlineElems) -- remove the extra LineBreak
  return pandoc.Span(inlineElems, pandoc.Attr("", {"footnote"}, {}))
end

If the Note element embeds other types of block elements (like a BulletList or a Table), one has to develop a specific filter for the walk_inline function.

like image 159
RLesur Avatar answered Sep 22 '22 01:09

RLesur