Skip to content

Instantly share code, notes, and snippets.

@jrnold
Last active April 23, 2023 04:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrnold/c74dc3f43c8c7f90519bfe724fc78ee1 to your computer and use it in GitHub Desktop.
Save jrnold/c74dc3f43c8c7f90519bfe724fc78ee1 to your computer and use it in GitHub Desktop.
Pandoc filter - math to png
--[[ pandoc filter to convert inline math to png images
Sses dvipng to convert inline and display math to png. There are better ways to handle math output,
but some platforms (e.g. Confluence) can only handle images.
Inspirations:
- https://github.com/pandoc/lua-filters/tree/master/math2svg
- The "Building images with tizk" example: https://pandoc.org/lua-filters.html#module-pandoc.template
- Sphinx imgmath from https://github.com/sphinx-doc/sphinx/blob/aee3c0ab75974790adf359a9c5089d1d781a6b21/sphinx/ext/imgmath.py#L1
--]]
local system = require 'pandoc.system'
local path = require 'pandoc.path'
local function latex(texfile)
-- Arguments from
local args = { "latex", "-interaction=nonstopmode", texfile }
os.execute(table.concat(args, " "))
end
local function dvi2png(dvifile, pngfile)
local args = { "dvipng", "-gamma", "1.5", "-D", "110", "-bg", "Transparent", "-T", "tight", "-z9", "-o",
pngfile, dvifile }
os.execute(table.concat(args, " "))
end
local function wrapmath(content, displaymath)
content = string.gsub(content, "^\n*(.-)\n*$", "%1") or ""
if displaymath then
return string.format("\\begin{displaymath}%s\\end{displaymath}", content)
else
return string.format("\\begin{math}%s\\end{math}", content)
end
end
-- values from https://github.com/sphinx-doc/sphinx/blob/master/sphinx/templates/imgmath/template.tex.jinja
local function math_doc_template(content, displaymath, fontsize, baselineskip)
local template = [[
\documentclass[12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{amssymb}
\usepackage{amsfonts}
\usepackage{anyfontsize}
\usepackage{bm}
\pagestyle{empty}
\renewcommand{\vec}[1]{\mathbf{#1}}
\newcommand{\Normal}{\mathsf{Normal}}
\newcommand{\Beta}{\mathsf{Normal}}
\newcommand{\Binomial}{\mathsf{Binomial}}
\newcommand{\E}{\mathbb{E}}
\newcommand{\SE}{\mathrm{SE}}
\newcommand{\SEhat}{\widehat{\mathrm{SE}}}
\begin{document}
\fontsize{%d}{%.1f}\selectfont
%s
\end{document}
]]
content = wrapmath(content, displaymath)
fontsize = fontsize or 12 -- Replace with actual font size
baselineskip = baselineskip or fontsize * 1.2 -- Replace with actual baseline skip
local tex = template:format(fontsize, baselineskip, content)
return tex
end
local function tex2png(src, outfile)
system.with_temporary_directory("math2png", function(tmpdir)
system.with_working_directory(tmpdir, function()
local texfile = "math.tex"
local dvifile = "math.dvi"
local pngfile = "math.png"
local f = io.open(texfile, "w")
f:write(src)
f:close()
latex(texfile)
dvi2png(dvifile, pngfile)
os.rename(pngfile, outfile)
end)
end)
end
local function file_exists(name)
local f = io.open(name, 'r')
if f ~= nil then
io.close(f)
return true
else
return false
end
end
function Math(elem)
-- full latex document from template
local displaymath = elem.mathtype == "DisplayMath"
local doc = math_doc_template(elem.text, displaymath)
-- print(doc)
-- sha of the doc. using the doc accounts for preamble changes
-- if the preamble changes, the sha will change and the image will be regenerated
local doc_sha = pandoc.utils.sha1(doc)
local filetype = "png"
local fbasename = doc_sha .. '.' .. filetype
local dst_dir = path.join({ system.get_working_directory(), "math_files" })
if not file_exists(dst_dir) then
system.make_directory(dst_dir, true)
end
local fname = path.join({ dst_dir, fbasename })
if not file_exists(fname) then
tex2png(doc, fname)
end
local cls
if displaymath then
cls = "math display"
else
cls = "math inline"
end
local img = pandoc.Span(pandoc.Image("", fname, "", { ["alt"] = elem.text }), { class = cls })
return img
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment