Files
tracy/manual/filter.lua
2026-06-06 01:44:27 +02:00

154 lines
6.2 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function Link(el)
el.attributes['reference-type'] = nil
el.attributes['reference'] = nil
return el
end
-- Drop Div wrappers (e.g. table/titlepage containers), keeping their content.
function Div(el)
return el.content
end
-- ---------------------------------------------------------------------------
-- LaTeX math -> plain-text approximation.
--
-- The target Markdown renderer has no math support, so a raw "$\frac{1}{2}$"
-- would show verbatim. We turn each math node into the closest Unicode/ASCII
-- equivalent: fractions become "a/b", \times becomes "x", super/subscripts use
-- Unicode digits, and the one multi-line display equation becomes a fenced
-- code block (Markdown collapses plain newlines, a code block keeps them).
-- ---------------------------------------------------------------------------
local sup = {['0']='',['1']='¹',['2']='²',['3']='³',['4']='',['5']='',
['6']='',['7']='',['8']='',['9']='',['+']='',['-']='',
['=']='',['(']='',[')']=''}
local sub = {['0']='',['1']='',['2']='',['3']='',['4']='',['5']='',
['6']='',['7']='',['8']='',['9']='',['+']='',['-']='',
['=']='',['(']='',[')']=''}
-- Symbol replacements, applied as literal substitutions. Longer commands must
-- precede those that are a prefix of them (e.g. \rightarrow before \right).
local symbols = {
{'\\leftrightarrow',''}, {'\\rightarrow',''}, {'\\leftarrow',''},
{'\\Rightarrow',''}, {'\\Leftarrow',''}, {'\\to',''}, {'\\mapsto',''},
{'\\times','×'}, {'\\cdot','·'}, {'\\div','÷'}, {'\\ast','*'}, {'\\star','*'},
{'\\leq',''}, {'\\geq',''}, {'\\neq',''}, {'\\approx',''}, {'\\equiv',''},
{'\\ll','«'}, {'\\gg','»'}, {'\\le',''}, {'\\ge',''},
{'\\ldots',''}, {'\\cdots',''}, {'\\dots',''}, {'\\infty',''},
{'\\pm','±'}, {'\\mp',''}, {'\\propto',''}, {'\\sum','Σ'}, {'\\prod','Π'},
{'\\alpha','α'}, {'\\beta','β'}, {'\\gamma','γ'}, {'\\delta','δ'}, {'\\Delta','Δ'},
{'\\mu','µ'}, {'\\sigma','σ'}, {'\\pi','π'}, {'\\lambda','λ'}, {'\\theta','θ'},
{'\\left',''}, {'\\right',''},
{'\\qquad',' '}, {'\\quad',' '}, {'\\,',' '}, {'\\;',' '}, {'\\:',' '},
{'\\ ',' '}, {'\\!',''},
{'\\%','%'}, {'\\#','#'}, {'\\&','&'}, {'\\_','_'}, {'\\{','{'}, {'\\}','}'},
{'\\$','$'},
}
-- Literal (non-pattern) string replacement; avoids Lua pattern magic in keys.
local function lit_replace(s, a, b)
local out, i = {}, 1
while true do
local p = s:find(a, i, true)
if not p then out[#out + 1] = s:sub(i); break end
out[#out + 1] = s:sub(i, p - 1)
out[#out + 1] = b
i = p + #a
end
return table.concat(out)
end
-- Strip the outer braces of a "%b{}" capture.
local function grp(b) return b:sub(2, #b - 1) end
-- Map a string to Unicode super/subscript, or nil if any char is unsupported.
local function map_script(txt, map)
local res = {}
for i = 1, #txt do
local c = txt:sub(i, i)
if not map[c] then return nil end
res[#res + 1] = map[c]
end
return table.concat(res)
end
local function convert(s)
-- Text/font wrappers: keep the content, recurse to handle nesting.
for _, cmd in ipairs({'text', 'mathrm', 'mathit', 'mathbf', 'mathbb',
'mathsf', 'mathtt', 'mathcal', 'operatorname',
'textbf', 'textit', 'textrm'}) do
s = s:gsub('\\' .. cmd .. '(%b{})', function(b) return convert(grp(b)) end)
end
-- Fractions -> "num/den" (spaced when either side has spaces).
local function frac(a, b)
local n, d = convert(grp(a)), convert(grp(b))
local sep = (n:find(' ', 1, true) or d:find(' ', 1, true)) and ' / ' or '/'
return n .. sep .. d
end
s = s:gsub('\\frac(%b{})(%b{})', frac)
s = s:gsub('\\dfrac(%b{})(%b{})', frac)
s = s:gsub('\\tfrac(%b{})(%b{})', frac)
s = s:gsub('\\sfrac(%b{})(%b{})', frac)
-- Roots.
s = s:gsub('\\sqrt(%b{})', function(b) return '√(' .. convert(grp(b)) .. ')' end)
-- Single-char scripts first, so the braced fallback (e.g. "_native") below
-- is not re-scanned and mangled into Unicode subscripts.
s = s:gsub('%^([%w])', function(c) return sup[c] or ('^' .. c) end)
s = s:gsub('_([%w])', function(c) return sub[c] or ('_' .. c) end)
-- Braced scripts: Unicode when the content is all digits/signs, else keep
-- a readable "^(...)" / "_..." form.
s = s:gsub('%^(%b{})', function(b)
local inner = convert(grp(b))
return map_script(inner, sup) or ('^(' .. inner .. ')')
end)
s = s:gsub('_(%b{})', function(b)
local inner = convert(grp(b))
return map_script(inner, sub) or ('_' .. inner)
end)
-- Remaining symbols.
for _, pair in ipairs(symbols) do s = lit_replace(s, pair[1], pair[2]) end
return s
end
-- Convert a display equation, preserving its line structure for a code block.
local function convert_display(s)
s = convert(s)
for _, env in ipairs({'cases', 'aligned', 'align', 'array', 'matrix',
'gathered', 'split'}) do
s = lit_replace(s, '\\begin{' .. env .. '}', '')
s = lit_replace(s, '\\end{' .. env .. '}', '')
end
s = lit_replace(s, '\\\\', '\n') -- row break
s = s:gsub('%s*&%s*', ' ') -- column separator -> spacing
local lines = {}
for line in (s .. '\n'):gmatch('(.-)\n') do
line = line:gsub('^%s+', ''):gsub('%s+$', '')
if line ~= '' then lines[#lines + 1] = line end
end
for i = 2, #lines do lines[i] = ' ' .. lines[i] end -- indent continuations
return table.concat(lines, '\n')
end
function Math(el)
if el.mathtype == 'DisplayMath' then
return el -- handled at block level by Para, to emit a code block
end
return pandoc.Str(convert(el.text))
end
-- A paragraph that is solely a display equation becomes a fenced code block.
function Para(el)
local maths, only_math = {}, true
for _, x in ipairs(el.content) do
if x.t == 'Math' and x.mathtype == 'DisplayMath' then
maths[#maths + 1] = x
elseif x.t ~= 'Space' and x.t ~= 'SoftBreak' and x.t ~= 'LineBreak' then
only_math = false
end
end
if #maths == 0 or not only_math then return nil end
local parts = {}
for _, m in ipairs(maths) do parts[#parts + 1] = convert_display(m.text) end
return pandoc.CodeBlock(table.concat(parts, '\n\n'))
end