-- Render content below header in three columns local multicol_begin = pandoc.RawBlock('latex', [[ \begin{multicols}{3}\raggedcolumns ]]) local multicol_end = pandoc.RawBlock('latex', [[ \end{multicols} ]]) local ad_template = [[ \begin{minipage}[%s][%s\textheight][c]{\textwidth} \centering \includegraphics[width=\linewidth,height=%s\textheight,keepaspectratio]{%s} \end{minipage} ]] local function has_class(elem, class) local classes = elem.classes if not classes then return false end for _, c in ipairs(classes) do if c == class then return true end end return false end local function is_columnar_header(elem) return elem.t == "Header" and elem.level == 1 end local function is_multicols_begin(elem) return elem.t == "RawBlock" and elem.text == "\\begin{multicols}{3}\\raggedcolumns" end local function is_multicols_end(elem) return elem.t == "RawBlock" and elem.text == "\\end{multicols}" end -- a standalone image with optional caption that has class .wide -- TODO: detect pre-quarto-filter pattern too local function is_wide(elem) return ((elem.t == "Para" and has_class(elem.content[1], "wide")) or (elem.t == "Figure" and has_class(elem.content[1].content[1], "wide"))) end -- TODO: detect pre-quarto-filter pattern too local function is_newpage_command(elem) return (elem.t == "RawBlock" and elem.text == "\\newpage{}") or (elem.t == "Para" and #elem.content == 5 and elem.content[3] == "pagebreak") end -- a paragraph where initial element has class .ad is an advertising local function is_advertising(elem) if elem.t == "Para" then local elem1 = elem.content[1] if elem1.t == "Image" and has_class(elem1, "ad") then return true end end return false end -- wrap images with class .ad to span half or full page local function ad(elem) local amount = #elem.content -- 1 ad, spanning full page except with class .top or .bottom if amount == 1 then local elem1 = elem.content[1] if elem1.t ~= "Image" or not elem1.src then error("failed to parse advertisement") return elem end if has_class(elem1, "top") then return pandoc.RawBlock('latex', "\\noindent\n" .. string.format(ad_template, "t", "0.5", "0.5", elem1.src)) elseif has_class(elem1, "bottom") then return pandoc.RawBlock('latex', "\\vspace*{\\fill}\\noindent\n" .. string.format(ad_template, "b", "0.5", "0.5", elem1.src)) end return pandoc.RawBlock('latex',"\\noindent\n" .. string.format(ad_template, "t", "1", "1", elem1.src)) end -- 2 ads spanning full page if amount == 3 then local elem1 = elem.content[1] local elem2 = elem.content[3] if elem1.t ~= "Image" or not elem1.src or elem2.t ~= "Image" or not elem2.src then error("failed to parse 2-element advertisement") return elem end return pandoc.RawBlock('latex', "\\noindent\n" .. string.format(ad_template, "t", "0.5", "0.5", elem1.src) .. string.format(ad_template, "b", "0.5", "0.5", elem2.src)) end error("failed to parse advertisement, wrong amount of elements: " .. amount) return elem end function Pandoc(doc) if FORMAT:match 'latex' then -- locate topmost-only elements where multicol state should change local multicol_now = false local multicol_places = {} for i, elem in ipairs(doc.blocks) do if is_columnar_header(elem) then multicol_places[i] = multicol_now and "renew" or "begin" multicol_now = true elseif is_wide(elem) then if multicol_now then multicol_places[i] = "renew" end elseif is_advertising(elem) then doc.blocks[i] = ad(elem) if multicol_now then multicol_places[i] = "renew" end elseif is_newpage_command(elem) then if multicol_now then multicol_places[i] = "end" multicol_now = false end elseif elem.t == "HorizontalRule" then if multicol_now then multicol_places[i] = "end" multicol_now = false end elseif is_multicols_begin(elem) then multicol_now = true elseif is_multicols_end(elem) then multicol_now = false end end if multicol_now then multicol_places[#doc.blocks] = "end" end -- apply multicol, in reverse order since it shifts later indices for i = #doc.blocks, 1, -1 do local state = multicol_places[i] if state ~= nil then if state == "begin" then doc.blocks:insert(i + 1, multicol_begin) elseif state == "renew" then doc.blocks:insert(i + 1, multicol_begin) doc.blocks:insert(i, multicol_end) elseif state == "end" then doc.blocks:insert(i, multicol_end) end end end if #multicol_places >= 1 then return doc end end end