diff options
Diffstat (limited to '_extensions/js/multicol-nohead/multicol-nohead.lua')
-rw-r--r-- | _extensions/js/multicol-nohead/multicol-nohead.lua | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/_extensions/js/multicol-nohead/multicol-nohead.lua b/_extensions/js/multicol-nohead/multicol-nohead.lua new file mode 100644 index 0000000..f190db8 --- /dev/null +++ b/_extensions/js/multicol-nohead/multicol-nohead.lua @@ -0,0 +1,166 @@ +-- 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 |