Last active
April 23, 2023 04:39
-
-
Save jrnold/c74dc3f43c8c7f90519bfe724fc78ee1 to your computer and use it in GitHub Desktop.
Pandoc filter - math to png
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ 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