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