filterluapandoccaption

pandoc md -> latex write lua-filter to change latex-macro used for caption


I have written a custom latex .cls file to establish a typesetting workflow for the scientific journals of my research institute. The texts should be written in Markdown and then be processed with pandoc to LaTeX.

I already have an elaborated pandoc template to produce the LaTeX preambel etc. So far its working great.

But for the figures I need the caption from the Markdown file to be set with \sidecaption instead of \caption in LaTeX, as well as with an optional argument (short-caption) for the image attribution in the list of figures.

To get the latter working I use the following template from a GitHub discussion in the pandoc repo:

PANDOC_VERSION:must_be_at_least '3.1'

if FORMAT:match 'latex' then
  function Figure(f)
    local short = f.content[1].content[1].attributes['short-caption']
    if short and not f.caption.short then
      f.caption.short = pandoc.Inlines(short)
    end
    return f
  end
end

That works without any flaws.

But now I need to figure out how to change the LaTeX macro used for the caption. The older approach of pre pandoc version 3.0 posted by @tarleb is really intuitive and I could have easily adapted it to my needs. But since pandoc 3.0 there is the new complex figures approach and, so far, I couldn't figure out how to change the LaTeX macro used for the captions with this new behaviour.

I tried something like that (Adapted from here:

if FORMAT:match 'latex' then
  function RawBlock (raw)
    local caption = raw.text:match('\\caption')
    if caption then
       raw:gsub('\\caption', '\\sidecaption')
    end
    return raw
  end
end

But nothing happened.

The main challenge for me are my more-or-less non-existing lua skills. I just never had to use it for my daily tasks. I thought about using awk or sed to edit the .tex file itself using a regex-substitution, but that should remain an absolute stopgap, since it makes the whole workflow less portable.

Thus, I'm hoping for a hint/a solution in form of a pandoc-lua script which 1. helps me to achieve the goal, and 2. improve my understanding of lua and the complex figures approach for similar future tasks.

Edit

The plain LaTeX solution suggested by Julien is not a real option. The \caption macro is very complex in the backend and cannot be copied on the fly via \let, \NewCommandCopy or something similar. Even after doing so with e.g. \NewCommandCopy{\oldcaption}{\caption} and then setting \RenewDocumentCommand{\caption}{o m}{\sidecaption[#1]{#2}} nothing changes and the definition of \caption, checked with \meaning or something similar, stays the same as before (even \DeclareDocumentCommand doesn't work).

In the end, it might be possible to somehow change the \caption macro itself. But the effort might not be worth the result (and its more of a question for TeX.SE).

There must be a simpler solution using Lua. Maybe the writer option also suggested by Julien is worth a try. But for that, my missing Lua skills remain as an obstacle. Right now, I'm using a sed script, which works just fine. But it limits the usage to Unix-Systems. Thus, a plain pandoc solution still is the goal to go.


Solution

  • There seems to be no easy way to alter or substitute the \caption macro with a Lua filter converting from Markdown to Latex using pandoc, because the Latex control sequence used for the caption is kind of hardcoded in the Haskell Latex writer.

    But there is another Lua solution using Lua(la)tex which enables the processing of Lua code directly from within the tex document. Therefore, I included the following lines into my custom Latex template called with the --template flag in pandoc:

    \ifLuaTeX
    \usepackage{luacode}
    \begin{luacode*}
     local count = 0
     function replace_caption_with_sidecaption(line)
       if string.find(line, "\\begin{figure}.*") then
         in_figure_env = true
       end
    
       if string.find(line, "\\end{figure}") then
         in_figure_env = false
       end
    
       if in_figure_env and string.find(line, "^(%s*)\\caption") then
         if count == 0 then
           line = line:gsub("\\caption", "\\sidecaption", 1)
           local count = count + 1
         end
       end
       return line
     end
    \end{luacode*}
    \directlua{luatexbase.add_to_callback("process_input_buffer", replace_caption_with_sidecaption, "replace_caption_with_sidecaption")}
    \fi
    

    This code substitutes evert \caption string with \sidecaption as long as it is located inside a figure environment and there the first macro of the line.

    It works great, but, of course, makes Lualatex the mandatory compilation program.