371 lines
9.0 KiB
Lua
371 lines
9.0 KiB
Lua
|
-- This is a gross hack of the sample custom writer for pandoc.
|
||
|
-- It produces output
|
||
|
-- that is very similar to that of pandoc's HTML writer.
|
||
|
-- There is one new feature: code blocks marked with class 'dot'
|
||
|
-- are piped through graphviz and images are included in the HTML
|
||
|
-- output using 'data:' URLs. The image format can be controlled
|
||
|
-- via the `image_format` metadata field.
|
||
|
--
|
||
|
-- Invoke with: pandoc -t gmi.lua
|
||
|
--
|
||
|
-- Note: you need not have lua installed on your system to use this
|
||
|
-- custom writer. However, if you do have lua installed, you can
|
||
|
-- use it to test changes to the script. 'lua sample.lua' will
|
||
|
-- produce informative error messages if your code contains
|
||
|
-- syntax errors.
|
||
|
|
||
|
local pipe = pandoc.pipe
|
||
|
local stringify = (require "pandoc.utils").stringify
|
||
|
|
||
|
-- The global variable PANDOC_DOCUMENT contains the full AST of
|
||
|
-- the document which is going to be written. It can be used to
|
||
|
-- configure the writer.
|
||
|
local meta = PANDOC_DOCUMENT.meta
|
||
|
|
||
|
-- Choose the image format based on the value of the
|
||
|
-- `image_format` meta value.
|
||
|
local image_format = meta.image_format and stringify(meta.image_format) or "png"
|
||
|
local image_mime_type =
|
||
|
({
|
||
|
jpeg = "image/jpeg",
|
||
|
jpg = "image/jpeg",
|
||
|
gif = "image/gif",
|
||
|
png = "image/png",
|
||
|
svg = "image/svg+xml"
|
||
|
})[image_format] or error("unsupported image format `" .. image_format .. "`")
|
||
|
|
||
|
-- Character escaping
|
||
|
local function escape(s, in_attribute)
|
||
|
return s:gsub(
|
||
|
'[<>&"\']',
|
||
|
function(x)
|
||
|
if x == "<" then
|
||
|
return "<"
|
||
|
elseif x == ">" then
|
||
|
return ">"
|
||
|
elseif x == "&" then
|
||
|
return "&"
|
||
|
elseif in_attribute and x == '"' then
|
||
|
return """
|
||
|
elseif in_attribute and x == "'" then
|
||
|
return "'"
|
||
|
else
|
||
|
return x
|
||
|
end
|
||
|
end
|
||
|
)
|
||
|
end
|
||
|
|
||
|
-- Helper function to convert an attributes table into
|
||
|
-- a string that can be put into HTML tags.
|
||
|
local function attributes(attr)
|
||
|
local attr_table = {}
|
||
|
for x, y in pairs(attr) do
|
||
|
if y and y ~= "" then
|
||
|
table.insert(attr_table, " " .. x .. '="' .. escape(y, true) .. '"')
|
||
|
end
|
||
|
end
|
||
|
return table.concat(attr_table)
|
||
|
end
|
||
|
|
||
|
-- Table to store footnotes, so they can be included at the end.
|
||
|
local notes = {}
|
||
|
local links = {}
|
||
|
|
||
|
-- Blocksep is used to separate block elements.
|
||
|
function Blocksep()
|
||
|
return "\n\n"
|
||
|
end
|
||
|
|
||
|
-- This function is called once for the whole document. Parameters:
|
||
|
-- body is a string, metadata is a table, variables is a table.
|
||
|
-- This gives you a fragment. You could use the metadata table to
|
||
|
-- fill variables in a custom lua template. Or, pass `--template=...`
|
||
|
-- to pandoc, and pandoc will do the template processing as usual.
|
||
|
function Doc(body, metadata, variables)
|
||
|
local buffer = {}
|
||
|
local function add(s)
|
||
|
table.insert(buffer, s)
|
||
|
end
|
||
|
add(body)
|
||
|
if #notes > 0 then
|
||
|
add("### footnotes")
|
||
|
for _, note in pairs(notes) do
|
||
|
add(note)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if #links > 0 then
|
||
|
add("### links")
|
||
|
for _, link in pairs(links) do
|
||
|
add(link)
|
||
|
end
|
||
|
end
|
||
|
return table.concat(buffer, "\n") .. "\n"
|
||
|
end
|
||
|
|
||
|
-- The functions that follow render corresponding pandoc elements.
|
||
|
-- s is always a string, attr is always a table of attributes, and
|
||
|
-- items is always an array of strings (the items in a list).
|
||
|
-- Comments indicate the types of other variables.
|
||
|
|
||
|
function Str(s)
|
||
|
return s
|
||
|
end
|
||
|
|
||
|
function Space()
|
||
|
return " "
|
||
|
end
|
||
|
|
||
|
function SoftBreak()
|
||
|
return " "
|
||
|
end
|
||
|
|
||
|
function LineBreak()
|
||
|
return "\n"
|
||
|
end
|
||
|
|
||
|
function Emph(s)
|
||
|
return "/" .. s .. "/"
|
||
|
end
|
||
|
|
||
|
function Strong(s)
|
||
|
return "*" .. s .. "*"
|
||
|
end
|
||
|
|
||
|
function Subscript(s)
|
||
|
return "_" .. s
|
||
|
end
|
||
|
|
||
|
function Superscript(s)
|
||
|
return "^" .. s
|
||
|
end
|
||
|
|
||
|
function SmallCaps(s)
|
||
|
return '<span style="font-variant: small-caps;">' .. s .. "</span>"
|
||
|
end
|
||
|
|
||
|
function Strikeout(s)
|
||
|
return " --- " .. s .. " --- "
|
||
|
end
|
||
|
|
||
|
function Link(s, tgt, tit, attr)
|
||
|
local text = s
|
||
|
local textIsLink = false
|
||
|
if string.find(s, "=>", 1, true) ~= nil then
|
||
|
textIsLink = true
|
||
|
s = string.sub(s, 2)
|
||
|
end
|
||
|
if not string.len(tit) == 0 then
|
||
|
text = s .. "(" .. tit .. ")"
|
||
|
end
|
||
|
local num = #links + 1
|
||
|
-- add link to the link table.
|
||
|
table.insert(links, "=> " .. escape(tgt, true) .. " [" .. num .. "] " .. text)
|
||
|
-- return the link text and ref number.
|
||
|
-- sthing not quite right here
|
||
|
if textIsLink then
|
||
|
return text .. " [" .. num .. "]"
|
||
|
end
|
||
|
|
||
|
return "<" .. text .. "> [" .. num .. "]"
|
||
|
end
|
||
|
|
||
|
function Image(s, src, tit, attr)
|
||
|
return "=> " .. src .. " " .. tit .. "\n"
|
||
|
end
|
||
|
|
||
|
function Code(s, attr)
|
||
|
return "`" .. s .. "`"
|
||
|
end
|
||
|
|
||
|
function InlineMath(s)
|
||
|
return "$" .. s .. "$"
|
||
|
end
|
||
|
|
||
|
function DisplayMath(s)
|
||
|
return "$" .. s + "\n$\n"
|
||
|
--return "\\[" .. escape(s) .. "\\]"
|
||
|
end
|
||
|
|
||
|
function SingleQuoted(s)
|
||
|
return '"' .. s .. '"'
|
||
|
end
|
||
|
|
||
|
function DoubleQuoted(s)
|
||
|
return "'" .. s .. "'"
|
||
|
end
|
||
|
|
||
|
function Note(s)
|
||
|
local num = #notes + 1
|
||
|
-- insert the back reference right before the final closing tag.
|
||
|
|
||
|
-- add note to the note table.
|
||
|
table.insert(notes, "[" .. num .. "] " .. s)
|
||
|
-- return the footnote reference, linked to the note.
|
||
|
return "[fn " .. num .. "]"
|
||
|
end
|
||
|
|
||
|
function Span(s, attr)
|
||
|
return s
|
||
|
end
|
||
|
|
||
|
function RawInline(format, str)
|
||
|
if format == "html" then
|
||
|
return str
|
||
|
else
|
||
|
return ""
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function Cite(s, cs)
|
||
|
return "\nsorry cite not implemented\n"
|
||
|
end
|
||
|
|
||
|
function Plain(s)
|
||
|
return s
|
||
|
end
|
||
|
|
||
|
function Para(s)
|
||
|
return s .. "\n"
|
||
|
end
|
||
|
|
||
|
-- lev is an integer, the header level.
|
||
|
function Header(lev, s, attr)
|
||
|
if lev == 1 then
|
||
|
return "# " .. s .. "\n"
|
||
|
elseif lev == 2 then
|
||
|
return "## " .. s .. "\n"
|
||
|
elseif lev == 3 then
|
||
|
return "### " .. s .. "\n"
|
||
|
else
|
||
|
return s .. "\n"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function BlockQuote(s)
|
||
|
return "> " .. s .. "\n"
|
||
|
end
|
||
|
|
||
|
function HorizontalRule()
|
||
|
return "```\n----------------------------------------------------------- <hr>\n```\n"
|
||
|
end
|
||
|
|
||
|
function LineBlock(ls)
|
||
|
return "LineBlock not implemented"
|
||
|
--return '<div style="white-space: pre-line;">' .. table.concat(ls, '\n') .. '</div>'
|
||
|
end
|
||
|
|
||
|
function CodeBlock(s, attr)
|
||
|
return "```\n" .. s .. "\n```"
|
||
|
end
|
||
|
|
||
|
function BulletList(items)
|
||
|
local buffer = {}
|
||
|
for _, item in pairs(items) do
|
||
|
table.insert(buffer, "* " .. item)
|
||
|
end
|
||
|
return table.concat(buffer, "\n")
|
||
|
end
|
||
|
|
||
|
function OrderedList(items)
|
||
|
local buffer = {}
|
||
|
for _, item in pairs(items) do
|
||
|
table.insert(buffer, "* " .. item)
|
||
|
end
|
||
|
return table.concat(buffer, "\n")
|
||
|
end
|
||
|
|
||
|
function DefinitionList(items)
|
||
|
local buffer = {}
|
||
|
for _, item in pairs(items) do
|
||
|
local k, v = next(item)
|
||
|
table.insert(buffer, "### " .. k .. "\n" .. table.concat(v, "\n"))
|
||
|
end
|
||
|
return table.concat(buffer, "\n")
|
||
|
end
|
||
|
|
||
|
-- Convert pandoc alignment to something HTML can use.
|
||
|
-- align is AlignLeft, AlignRight, AlignCenter, or AlignDefault.
|
||
|
local function html_align(align)
|
||
|
if align == "AlignLeft" then
|
||
|
return "left"
|
||
|
elseif align == "AlignRight" then
|
||
|
return "right"
|
||
|
elseif align == "AlignCenter" then
|
||
|
return "center"
|
||
|
else
|
||
|
return "left"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function CaptionedImage(src, tit, caption, attr)
|
||
|
return '<div class="figure">\n<img src="' ..
|
||
|
escape(src, true) ..
|
||
|
'" title="' .. escape(tit, true) .. '"/>\n' .. '<p class="caption">' .. escape(caption) .. "</p>\n</div>"
|
||
|
end
|
||
|
|
||
|
-- Caption is a string, aligns is an array of strings,
|
||
|
-- widths is an array of floats, headers is an array of
|
||
|
-- strings, rows is an array of arrays of strings.
|
||
|
function Table(caption, aligns, widths, headers, rows)
|
||
|
-- finding out more about lua than I wanted to here...
|
||
|
local function dQuote(value)
|
||
|
if value == nil then
|
||
|
return nil
|
||
|
end
|
||
|
return '"' .. value .. '"'
|
||
|
end
|
||
|
|
||
|
-- accumulate table csv in string res
|
||
|
local res = "```" .. caption .. "\n"
|
||
|
-- get the top line
|
||
|
for i, col in pairs(headers) do
|
||
|
if i > 1 then
|
||
|
res = res .. ","
|
||
|
end
|
||
|
res = res .. dQuote(col)
|
||
|
end
|
||
|
res = res .. "\n"
|
||
|
|
||
|
-- get the rest of the table
|
||
|
for i, curRow in pairs(rows) do
|
||
|
for i, col in pairs(curRow) do
|
||
|
if i > 1 then
|
||
|
res = res .. ","
|
||
|
end
|
||
|
res = res .. dQuote(col)
|
||
|
end
|
||
|
res = res .. "\n"
|
||
|
end
|
||
|
res = res .. "``` "
|
||
|
|
||
|
-- send it to pandoc
|
||
|
return res
|
||
|
end
|
||
|
|
||
|
function RawBlock(format, str)
|
||
|
if format == "html" then
|
||
|
return str
|
||
|
else
|
||
|
return ""
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function Div(s, attr)
|
||
|
return s
|
||
|
--return "<div" .. attributes(attr) .. ">\n" .. s .. "</div>"
|
||
|
end
|
||
|
|
||
|
-- The following code will produce runtime warnings when you haven't defined
|
||
|
-- all of the functions you need for the custom writer, so it's useful
|
||
|
-- to include when you're working on a writer.
|
||
|
local meta = {}
|
||
|
meta.__index = function(_, key)
|
||
|
io.stderr:write(string.format("WARNING: Undefined function '%s'\n", key))
|
||
|
return function()
|
||
|
return ""
|
||
|
end
|
||
|
end
|
||
|
setmetatable(_G, meta)
|