summaryrefslogtreecommitdiff
path: root/_extensions/js/multicol-nohead/multicol-nohead.lua
diff options
context:
space:
mode:
Diffstat (limited to '_extensions/js/multicol-nohead/multicol-nohead.lua')
-rw-r--r--_extensions/js/multicol-nohead/multicol-nohead.lua166
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