summaryrefslogtreecommitdiff
path: root/_extensions/js/multicol-nohead/multicol-nohead.lua
blob: f190db897b19293c17e61516bce43ff39abbb5e6 (plain)
  1. -- Render content below header in three columns
  2. local multicol_begin = pandoc.RawBlock('latex', [[
  3. \begin{multicols}{3}\raggedcolumns
  4. ]])
  5. local multicol_end = pandoc.RawBlock('latex', [[
  6. \end{multicols}
  7. ]])
  8. local ad_template = [[
  9. \begin{minipage}[%s][%s\textheight][c]{\textwidth}
  10. \centering
  11. \includegraphics[width=\linewidth,height=%s\textheight,keepaspectratio]{%s}
  12. \end{minipage}
  13. ]]
  14. local function has_class(elem, class)
  15. local classes = elem.classes
  16. if not classes then
  17. return false
  18. end
  19. for _, c in ipairs(classes) do
  20. if c == class then
  21. return true
  22. end
  23. end
  24. return false
  25. end
  26. local function is_columnar_header(elem)
  27. return elem.t == "Header" and elem.level == 1
  28. end
  29. local function is_multicols_begin(elem)
  30. return elem.t == "RawBlock" and elem.text == "\\begin{multicols}{3}\\raggedcolumns"
  31. end
  32. local function is_multicols_end(elem)
  33. return elem.t == "RawBlock" and elem.text == "\\end{multicols}"
  34. end
  35. -- a standalone image with optional caption that has class .wide
  36. -- TODO: detect pre-quarto-filter pattern too
  37. local function is_wide(elem)
  38. return ((elem.t == "Para" and has_class(elem.content[1], "wide"))
  39. or (elem.t == "Figure" and has_class(elem.content[1].content[1], "wide")))
  40. end
  41. -- TODO: detect pre-quarto-filter pattern too
  42. local function is_newpage_command(elem)
  43. return (elem.t == "RawBlock" and elem.text == "\\newpage{}")
  44. or (elem.t == "Para" and #elem.content == 5 and elem.content[3] == "pagebreak")
  45. end
  46. -- a paragraph where initial element has class .ad is an advertising
  47. local function is_advertising(elem)
  48. if elem.t == "Para" then
  49. local elem1 = elem.content[1]
  50. if elem1.t == "Image" and has_class(elem1, "ad") then
  51. return true
  52. end
  53. end
  54. return false
  55. end
  56. -- wrap images with class .ad to span half or full page
  57. local function ad(elem)
  58. local amount = #elem.content
  59. -- 1 ad, spanning full page except with class .top or .bottom
  60. if amount == 1 then
  61. local elem1 = elem.content[1]
  62. if elem1.t ~= "Image" or not elem1.src then
  63. error("failed to parse advertisement")
  64. return elem
  65. end
  66. if has_class(elem1, "top") then
  67. return pandoc.RawBlock('latex', "\\noindent\n"
  68. .. string.format(ad_template, "t", "0.5", "0.5", elem1.src))
  69. elseif has_class(elem1, "bottom") then
  70. return pandoc.RawBlock('latex', "\\vspace*{\\fill}\\noindent\n"
  71. .. string.format(ad_template, "b", "0.5", "0.5", elem1.src))
  72. end
  73. return pandoc.RawBlock('latex',"\\noindent\n"
  74. .. string.format(ad_template, "t", "1", "1", elem1.src))
  75. end
  76. -- 2 ads spanning full page
  77. if amount == 3 then
  78. local elem1 = elem.content[1]
  79. local elem2 = elem.content[3]
  80. if elem1.t ~= "Image" or not elem1.src
  81. or elem2.t ~= "Image" or not elem2.src
  82. then
  83. error("failed to parse 2-element advertisement")
  84. return elem
  85. end
  86. return pandoc.RawBlock('latex', "\\noindent\n"
  87. .. string.format(ad_template, "t", "0.5", "0.5", elem1.src)
  88. .. string.format(ad_template, "b", "0.5", "0.5", elem2.src))
  89. end
  90. error("failed to parse advertisement, wrong amount of elements: "
  91. .. amount)
  92. return elem
  93. end
  94. function Pandoc(doc)
  95. if FORMAT:match 'latex' then
  96. -- locate topmost-only elements where multicol state should change
  97. local multicol_now = false
  98. local multicol_places = {}
  99. for i, elem in ipairs(doc.blocks) do
  100. if is_columnar_header(elem) then
  101. multicol_places[i] = multicol_now and "renew" or "begin"
  102. multicol_now = true
  103. elseif is_wide(elem) then
  104. if multicol_now then
  105. multicol_places[i] = "renew"
  106. end
  107. elseif is_advertising(elem) then
  108. doc.blocks[i] = ad(elem)
  109. if multicol_now then
  110. multicol_places[i] = "renew"
  111. end
  112. elseif is_newpage_command(elem) then
  113. if multicol_now then
  114. multicol_places[i] = "end"
  115. multicol_now = false
  116. end
  117. elseif elem.t == "HorizontalRule" then
  118. if multicol_now then
  119. multicol_places[i] = "end"
  120. multicol_now = false
  121. end
  122. elseif is_multicols_begin(elem) then
  123. multicol_now = true
  124. elseif is_multicols_end(elem) then
  125. multicol_now = false
  126. end
  127. end
  128. if multicol_now then
  129. multicol_places[#doc.blocks] = "end"
  130. end
  131. -- apply multicol, in reverse order since it shifts later indices
  132. for i = #doc.blocks, 1, -1 do
  133. local state = multicol_places[i]
  134. if state ~= nil then
  135. if state == "begin" then
  136. doc.blocks:insert(i + 1, multicol_begin)
  137. elseif state == "renew" then
  138. doc.blocks:insert(i + 1, multicol_begin)
  139. doc.blocks:insert(i, multicol_end)
  140. elseif state == "end" then
  141. doc.blocks:insert(i, multicol_end)
  142. end
  143. end
  144. end
  145. if #multicol_places >= 1 then
  146. return doc
  147. end
  148. end
  149. end