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