Compare commits

...

4 Commits

Author SHA1 Message Date
Marcel Fabian Krüger 23c6714e97 align support and fixes 2021-04-24 16:53:50 +02:00
Marcel Fabian Krüger c0a1f60962 Better embellished operator handling 2021-04-23 07:50:21 +02:00
Marcel Fabian Krüger d6bda3aff3 Reorganize and add missing file 2021-04-23 02:59:35 +02:00
Marcel Fabian Krüger 8be2760919 Don't require unicode-math 2021-04-22 23:38:28 +02:00
12 changed files with 466 additions and 103 deletions

84
luamml-amsmath.lua Normal file
View File

@ -0,0 +1,84 @@
local write_xml = require'luamml-xmlwriter'
local make_root = require'luamml-convert'.make_root
local properties = node.get_properties_table()
local funcid = luatexbase.new_luafunction'luamml_amsmath_add_box_to_row:'
token.set_lua('luamml_amsmath_add_box_to_row:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
-- local box = token.scan_int()
local boxnum = 0
local startmath = tex.box[boxnum].list
assert(startmath.id == node.id"math")
local props = assert(properties[startmath])
local mml = assert(props.saved_mathml_table)
table.insert(mml, 1, {[0] = 'maligngroup'})
if mml[0] == 'mstyle' and mml.displaystyle == true then
mml[0], mml.displaystyle, mml.scriptlevel = 'mtd', nil, nil
else
if mml[0] ~= 'mstyle' then
mml = {[0] = 'mstyle', displaystyle = false, mml}
end
mml = {[0] = 'mtd', mml}
end
local row_temp = tex.nest[tex.nest.ptr-1]
print(row_temp)
props = properties[row_temp]
if not props then
props = {}
properties[row_temp] = props
end
if not props.mathml_row then
props.mathml_row = {[0] = 'mtr'}
end
mml_row = props.mathml_row
table.insert(mml_row, mml)
end
local funcid = luatexbase.new_luafunction'luamml_amsmath_finalize_row:'
token.set_lua('luamml_amsmath_finalize_row:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local row_temp = tex.nest[tex.nest.ptr-1]
print("final", row_temp)
local props = properties[row_temp]
if not props then return end
if not props.mathml_row then return end
mml_row = props.mathml_row
props = properties[tex.lists.align_head]
if not props then
props = {}
properties[tex.lists.align_head] = props
end
local mml_table = props.mathml_table_node_table
if not mml_table then
mml_table = {[0] = 'mtable', displaystyle = true}
props.mathml_table_node_table = mml_table
end
table.insert(mml_table, mml_row)
end
local funcid = luatexbase.new_luafunction'luamml_amsmath_finalize_table:'
token.set_lua('luamml_amsmath_finalize_table:', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local props = properties[tex.lists.align_head]
if not props then return end
local mml_table = props.mathml_table_node_table
if not mml_table then return end
print(write_xml(make_root(mml_table, 2)))
end
funcid = luatexbase.new_luafunction'luamml_last_math_alignmark:'
token.set_lua('luamml_last_math_alignmark:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local n = tex.nest.top.tail
n = n.nucleus or n
local props = properties[n]
if not props then
props = {}
properties[n] = props
end
props.mathml_table = {[0] = 'malignmark'}
end

View File

@ -1,5 +1,5 @@
local remap_comb = require'remap_comb' local remap_comb = require'luamml-data-combining'
local stretchy = require'stretchy' local stretchy = require'luamml-data-stretchy'
local properties = node.get_properties_table() local properties = node.get_properties_table()
@ -14,18 +14,38 @@ local noad_sub = node.subtypes'noad'
local radical_sub = node.subtypes'radical' local radical_sub = node.subtypes'radical'
local fence_sub = node.subtypes'fence' local fence_sub = node.subtypes'fence'
local remap_lookup = setmetatable({}, {__index = function(t, k)
local ch = utf8.char(k & 0x1FFFFF)
t[k] = ch
return ch
end})
local digit_map = {["0"] = true, ["1"] = true,
["2"] = true, ["3"] = true, ["4"] = true,
["5"] = true, ["6"] = true, ["7"] = true,
["8"] = true, ["9"] = true,}
-- Two marker tables. They are used instead of an embellished operator to mark space-like or user provided constructs
local user_provided, space_like = {}, {}
local nodes_to_table local nodes_to_table
local function sub_style(s) return s//4*2+5 end local function sub_style(s) return s//4*2+5 end
local function sup_style(s) return s//4*2+4+s%2 end local function sup_style(s) return s//4*2+4+s%2 end
-- The _to_table functions generally return a second argument which is
-- could be (if it were a <mo>) a core operator of the embellishe operator
-- or space_like/user_provided
-- The delim_to_table and acc_to_table are special since their return value should
-- always be considered a core operator
-- We ignore large_... since they aren't used for modern fonts -- We ignore large_... since they aren't used for modern fonts
local function delim_to_table(delim) local function delim_to_table(delim)
if not delim then return end if not delim then return end
local props = properties[delim] props = props and props.mathml_table local props = properties[delim] props = props and props.mathml_table
if props then return props end if props then return props end
local fam = delim.small_fam local fam = delim.small_fam
return {[0] = 'mo', utf8.char(delim.small_char), ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[delim.small_char] or nil } local char = remap_lookup[fam << 21 | delim.small_char]
return {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil }
end end
-- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars -- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars
@ -36,9 +56,9 @@ local function acc_to_table(acc, cur_style, stretch)
if acc.id ~= math_char_t then if acc.id ~= math_char_t then
error'confusion' error'confusion'
end end
local char = utf8.char(acc.char)
char = remap_comb[char] or char
local fam = acc.fam local fam = acc.fam
local char = remap_lookup[fam << 21 | acc.char]
char = remap_comb[char] or char
if stretch ~= not stretchy[char] then -- Handle nil gracefully in stretchy if stretch ~= not stretchy[char] then -- Handle nil gracefully in stretchy
stretch = nil stretch = nil
end end
@ -48,15 +68,21 @@ end
local function kernel_to_table(kernel, cur_style) local function kernel_to_table(kernel, cur_style)
if not kernel then return end if not kernel then return end
local props = properties[kernel] props = props and props.mathml_table local props = properties[kernel] props = props and props.mathml_table
if props then return props end if props then return props, user_provided end
local id = kernel.id local id = kernel.id
if id == math_char_t then if id == math_char_t then
local char = kernel.char
local elem = char >= 0x30 and char < 0x39 and 'mn' or 'mi'
local fam = kernel.fam local fam = kernel.fam
return {[0] = elem, utf8.char(char), ['tex:family'] = fam ~= 0 and fam or nil, mathvariant = char < 0x10000 and 'normal' or nil } local char = remap_lookup[fam << 21 | kernel.char]
local elem = digit_map[char] and 'mn' or 'mi'
local result = {[0] = elem,
char,
['tex:family'] = fam ~= 0 and fam or nil,
mathvariant = #char == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil
}
return result, result
elseif id == sub_box_t then elseif id == sub_box_t then
return {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list}} local result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list}}
return result, result
elseif id == sub_mlist_t then elseif id == sub_mlist_t then
return nodes_to_table(kernel.list, cur_style) return nodes_to_table(kernel.list, cur_style)
else else
@ -64,34 +90,34 @@ local function kernel_to_table(kernel, cur_style)
end end
end end
local function do_sub_sup(t, n, cur_style) local function do_sub_sup(t, core, n, cur_style)
local sub = kernel_to_table(n.sub, sub_style(cur_style)) local sub = kernel_to_table(n.sub, sub_style(cur_style))
local sup = kernel_to_table(n.sup, sup_style(cur_style)) local sup = kernel_to_table(n.sup, sup_style(cur_style))
if sub then if sub then
if sup then if sup then
return {[0] = 'msubsup', t, sub, sup} return {[0] = 'msubsup', t, sub, sup}, core
else else
return {[0] = 'msub', t, sub} return {[0] = 'msub', t, sub}, core
end end
elseif sup then elseif sup then
return {[0] = 'msup', t, sup} return {[0] = 'msup', t, sup}, core
else else
return t return t, core
end end
end end
local function noad_to_table(noad, sub, cur_style) local function noad_to_table(noad, sub, cur_style)
local class = noad_sub[sub] local class = noad_sub[sub]
local nucleus = kernel_to_table(noad.nucleus, class == 'over' and cur_style//2*2+1 or cur_style) local nucleus, core = kernel_to_table(noad.nucleus, class == 'over' and cur_style//2*2+1 or cur_style)
if class == 'ord' then if class == 'ord' then
elseif class == 'opdisplaylimits' or class == 'oplimits' or class == 'opnolimits' or class == 'bin' or class == 'rel' or class == 'open' elseif class == 'opdisplaylimits' or class == 'oplimits' or class == 'opnolimits' or class == 'bin' or class == 'rel' or class == 'open'
or class == 'close' or class == 'punct' or class == 'inner' then or class == 'close' or class == 'punct' or class == 'inner' then
if nucleus[0] == 'mrow' then if not core or not core[0] then
-- TODO -- TODO
else else
nucleus[0] = 'mo' core[0] = 'mo'
if stretchy[nucleus[1]] then nucleus.stretchy = false end if stretchy[core[1]] then core.stretchy = false end
if nucleus.mathvariant == 'normal' then nucleus.mathvariant = nil end if core.mathvariant == 'normal' then core.mathvariant = nil end
end end
nucleus['tex:class'] = class nucleus['tex:class'] = class
@ -103,35 +129,35 @@ local function noad_to_table(noad, sub, cur_style)
nucleus, nucleus,
sub or sup, sub or sup,
sub and sup, sub and sup,
} }, core
end end
elseif class == 'under' then elseif class == 'under' then
return {[0] = 'munder', return {[0] = 'munder',
nucleus, nucleus,
{[0] = 'mo', '_',}, {[0] = 'mo', '_',},
} }, core
elseif class == 'over' then elseif class == 'over' then
return {[0] = 'mover', return {[0] = 'mover',
nucleus, nucleus,
{[0] = 'mo', '\u{203E}',}, {[0] = 'mo', '\u{203E}',},
} }, core
elseif class == 'vcenter' then elseif class == 'vcenter' then
nucleus['tex:TODO'] = class nucleus['tex:TODO'] = class
else else
error[[confusion]] error[[confusion]]
end end
return do_sub_sup(nucleus, noad, cur_style) return do_sub_sup(nucleus, core, noad, cur_style)
end end
local function accent_to_table(accent, sub, cur_style) local function accent_to_table(accent, sub, cur_style)
local nucleus = kernel_to_table(accent.nucleus, cur_style//2*2+1) local nucleus, core = kernel_to_table(accent.nucleus, cur_style//2*2+1)
local top_acc = acc_to_table(accent.accent, cur_style, sub & 1 == 1) local top_acc = acc_to_table(accent.accent, cur_style, sub & 1 == 1)
local bot_acc = acc_to_table(accent.bot_accent, cur_style, sub & 2 == 2) local bot_acc = acc_to_table(accent.bot_accent, cur_style, sub & 2 == 2)
return {[0] = top_acc and (bot_acc and 'munderover' or 'mover') or 'munder', return {[0] = top_acc and (bot_acc and 'munderover' or 'mover') or 'munder',
nucleus, nucleus,
bot_acc or top_acc, bot_acc or top_acc,
bot_acc and top_acc, bot_acc and top_acc,
} }, core
end end
local style_table = { local style_table = {
@ -148,19 +174,19 @@ style_table.crampedscript, style_table.crampedscriptscript =
local function radical_to_table(radical, sub, cur_style) local function radical_to_table(radical, sub, cur_style)
local kind = radical_sub[sub] local kind = radical_sub[sub]
local nucleus = kernel_to_table(radical.nucleus, cur_style//2*2+1) local nucleus, core = kernel_to_table(radical.nucleus, cur_style//2*2+1)
local left = delim_to_table(radical.left) local left = delim_to_table(radical.left)
local elem local elem
if kind == 'radical' or kind == 'uradical' then if kind == 'radical' or kind == 'uradical' then
-- FIXME: Check that this is really a square root -- FIXME: Check that this is really a square root
elem = {[0] = 'msqrt', nucleus} elem, core = {[0] = 'msqrt', nucleus}, nil
elseif kind == 'uroot' then elseif kind == 'uroot' then
-- FIXME: Check that this is really a root -- FIXME: Check that this is really a root
elem = {[0] = 'msqrt', nucleus, kernel_to_table(radical.degree)} elem, core = {[0] = 'msqrt', nucleus, kernel_to_table(radical.degree)}, nil
elseif kind == 'uunderdelimiter' then elseif kind == 'uunderdelimiter' then
elem = {[0] = 'munder', left, nucleus} elem, core = {[0] = 'munder', left, nucleus}, left
elseif kind == 'uoverdelimiter' then elseif kind == 'uoverdelimiter' then
elem = {[0] = 'mover', left, nucleus} elem, core = {[0] = 'mover', left, nucleus}, left
elseif kind == 'udelimiterunder' then elseif kind == 'udelimiterunder' then
elem = {[0] = 'munder', nucleus, left} elem = {[0] = 'munder', nucleus, left}
elseif kind == 'udelimiterover' then elseif kind == 'udelimiterover' then
@ -168,11 +194,11 @@ local function radical_to_table(radical, sub, cur_style)
else else
error[[confusion]] error[[confusion]]
end end
return do_sub_sup(elem, radical, cur_style) return do_sub_sup(elem, core, radical, cur_style)
end end
local function fraction_to_table(fraction, sub, cur_style) local function fraction_to_table(fraction, sub, cur_style)
local num = kernel_to_table(fraction.num, sup_style(cur_style)) local num, core = kernel_to_table(fraction.num, sup_style(cur_style))
local denom = kernel_to_table(fraction.denom, sub_style(cur_style)) local denom = kernel_to_table(fraction.denom, sub_style(cur_style))
local left = delim_to_table(fraction.left) local left = delim_to_table(fraction.left)
local right = delim_to_table(fraction.right) local right = delim_to_table(fraction.right)
@ -194,23 +220,24 @@ local function fraction_to_table(fraction, sub, cur_style)
right, right,
} }
else else
return mfrac return mfrac, core
end end
end end
local function fence_to_table(fence, sub, cur_style) local function fence_to_table(fence, sub, cur_style)
local delim = delim_to_table(fence.delimiter) local delim = delim_to_table(fence.delimiter)
delim.fence = 'true' delim.fence = 'true'
return delim return delim, delim
end end
local function space_to_table(amount, sub, cur_style) local function space_to_table(amount, sub, cur_style)
if amount == 0 then return end if amount == 0 then return end
-- FIXME: What does MathML do in subscripts etc.? Probably we have to "unscale" in the non mu case...
if sub == 99 then -- TODO magic number if sub == 99 then -- TODO magic number
return {[0] = 'mspace', width = string.format("%.2fem", amount/18)} -- 18*2^16=1179648
return {[0] = 'mspace', width = string.format("%.2fem", amount/1179648)}, space_like
else else
return {[0] = 'mspace', width = string.format("%.2fem", amount/tex.sp'1em')} -- 65781.76=tex.sp'100bp'/100
return {[0] = 'mspace', width = string.format("%.2fpt", amount/65781.76)}, space_like
end end
end end
@ -218,64 +245,93 @@ function nodes_to_table(head, cur_style)
local t = {[0] = "mrow"} local t = {[0] = "mrow"}
local result = t local result = t
local nonscript local nonscript
local core = space_like
for n, id, sub in node.traverse(head) do for n, id, sub in node.traverse(head) do
local new_core
local props = properties[n] props = props and props.mathml_table local props = properties[n] props = props and props.mathml_table
if props then if props then
t[#t+1] = props t[#t+1], new_core = props, user_provided
elseif id == noad_t then elseif id == noad_t then
t[#t+1] = noad_to_table(n, sub, cur_style) t[#t+1], new_core = noad_to_table(n, sub, cur_style)
elseif id == accent_t then elseif id == accent_t then
t[#t+1] = accent_to_table(n, sub, cur_style) t[#t+1], new_core = accent_to_table(n, sub, cur_style)
elseif id == style_t then elseif id == style_t then
if #t ~= 0 then if sub ~= cur_style then
local new_t = {[0] = 'mstyle'} if #t == 0 then
t[#t+1] = new_t t[0] = 'mstyle'
t = new_t else
local new_t = {[0] = 'mstyle'}
t[#t+1] = new_t
t = new_t
end
if sub < 2 then
t.displaystyle, t.scriptlevel = true, 0
else
t.displaystyle, t.scriptlevel = false, sub//2 - 1
end
cur_style = sub
end end
if sub < 2 then new_core = space_like
t.displaystyle, t.scriptlevel = true, 0
else
t.displaystyle, t.scriptlevel = false, sub//2 - 1
end
cur_style = sub
elseif id == choice_t then elseif id == choice_t then
local size = cur_style//2 local size = cur_style//2
t[#t+1] = nodes_to_table(n[size == 0 and 'display' or size == 1 and 'text' t[#t+1], new_core = nodes_to_table(n[size == 0 and 'display'
or size == 2 and 'script' or size == 1 and 'text'
or size == 3 and 'scriptscript' or assert(false)], 2*size) or size == 2 and 'script'
or size == 3 and 'scriptscript'
or assert(false)], 2*size), space_like
elseif id == radical_t then elseif id == radical_t then
t[#t+1] = radical_to_table(n, sub, cur_style) t[#t+1], new_core = radical_to_table(n, sub, cur_style)
elseif id == fraction_t then elseif id == fraction_t then
t[#t+1] = fraction_to_table(n, sub, cur_style) t[#t+1], new_core = fraction_to_table(n, sub, cur_style)
elseif id == fence_t then elseif id == fence_t then
t[#t+1] = fence_to_table(n, sub, cur_style) t[#t+1], new_core = fence_to_table(n, sub, cur_style)
elseif id == kern_t then elseif id == kern_t then
if not nonscript then if not nonscript then
t[#t+1] = space_to_table(n.kern, sub, cur_style) t[#t+1], new_core = space_to_table(n.kern, sub, cur_style)
end end
elseif id == glue_t then elseif id == glue_t then
if cur_style >= 4 or not nonscript then if cur_style >= 4 or not nonscript then
if sub == 98 then -- TODO magic number if sub == 98 then -- TODO magic number
nonscript = true nonscript = true
else else
t[#t+1] = space_to_table(n.width, sub, cur_style) t[#t+1], new_core = space_to_table(n.width, sub, cur_style)
end end
end end
else else
t[#t+1] = {[0] = 'tex:TODO', other = n} new_core = {[0] = 'tex:TODO', other = n}
t[#t+1] = new_core
end end
nonscript = nil nonscript = nil
if core and new_core ~= space_like then
core = new_core
end
end end
return result return result, core
end end
return function(head, style) local function register_remap(family, mapping)
local result = nodes_to_table(head, style or 0) family = family << 21
result[0] = 'math' for from, to in next, mapping do
result.xmlns = 'http://www.w3.org/1998/Math/MathML' remap_lookup[family | from] = utf8.char(to)
result['xmlns:tex'] = 'http://typesetting.eu/2021/LuaMathML'
if style == 2 then
result.display = 'block'
end end
return result
end end
local function to_math(root, style)
if root[0] == 'mrow' then
root[0] = 'math'
else
root = {[0] = 'math', root}
end
root.xmlns = 'http://www.w3.org/1998/Math/MathML'
root['xmlns:tex'] = 'http://typesetting.eu/2021/LuaMathML'
if style < 2 then
root.display = 'block'
end
return root
end
return {
register_family = register_remap,
process = function(head, style) return nodes_to_table(head, style or 2) end,
make_root = to_math,
}

View File

@ -0,0 +1,51 @@
-- local remap_ot1 = {
-- 0x0393, 0x0394, 0x0398, 0x039B, 0x039E, 0x03A0, 0x03A3, 0x03A5,
-- 0x03A6, 0x03A8, 0x03A9, nil, nil, nil, nil, nil,
-- }
local remap_oml = { [0] =
-- Greek italic
0x1D6E4, 0x1D6E5, 0x1D6E9, 0x1D6EC, 0x1D6EF, 0x1D6F1, 0x1D6F4, 0x1D6F6,
0x1D6F7, 0x1D6F9, 0x1D6FA, 0x1D6FC, 0x1D6FD, 0x1D6FE, 0x1D6FF, 0x1D716,
0x1D701, 0x1D702, 0x1D703, 0x1D704, 0x1D705, 0x1D706, 0x1D707, 0x1D708,
0x1D709, 0x1D70B, 0x1D70C, 0x1D70E, 0x1D70F, 0x1D710, 0x1D719, 0x1D712,
0x1D713, 0x1D714, 0x1D700, 0x1D717, 0x1D71B, 0x1D71A, 0x1D70D, 0x1D711,
-- Symbols. (The nils are hook parts)
0x21BC, 0x21BD, 0x21C0, 0x21C1, nil, nil, 0x22BB, 0x22BC,
-- old style numerals (nobody should ever use these in math) and some punctuation
nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, 0x2E, 0x2C, 0x3C, 0x2F, 0x3E, 0x226D,
-- letters filled up with symbols
0x2202, 0x1D434, 0x1D435, 0x1D436, 0x1D437, 0x1D438, 0x1D439, 0x1D43A,
0x1D43B, 0x1D43C, 0x1D43D, 0x1D43E, 0x1D43F, 0x1D440, 0x1D441, 0x1D442,
0x1D443, 0x1D444, 0x1D445, 0x1D446, 0x1D447, 0x1D448, 0x1D449, 0x1D44A,
0x1D44B, 0x1D44C, 0x1D44D, 0x266D, 0x266E, 0x266F, 0x2323, 0x2322,
0x2113, 0x1D44E, 0x1D44F, 0x1D450, 0x1D451, 0x1D452, 0x1D453, 0x1D454,
0x210E, 0x1D456, 0x1D457, 0x1D458, 0x1D459, 0x1D45A, 0x1D45B, 0x1D45C,
0x1D45D, 0x1D45E, 0x1D45F, 0x1D460, 0x1D461, 0x1D462, 0x1D463, 0x1D464,
0x1D465, 0x1D466, 0x1D467, 0x1D6A4, 0x1D6A5, 0x2118, 0x2192, nil,
}
local remap_oms = { [0] =
0x2212, 0x22C5, 0xD7, 0x2A, 0xF7, 0x22C4, 0xB1, 0x2213,
0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x25CB, 0x2218, 0x2219,
0x224D, 0x2261, 0x2286, 0x2287, 0x2264, 0x2265, 0x2AAF, 0x2AB0,
0x223C, 0x2248, 0x2282, 0x2283, 0x226A, 0x226B, 0x227A, 0x227B,
0x2190, 0x2192, 0x2191, 0x2193, 0x2194, 0x2197, 0x2198, 0x2243,
0x21D0, 0x21D2, 0x21D1, 0x21D3, 0x21D4, 0x2196, 0x2199, 0x221D,
0x2032, 0x221E, 0x2208, 0x220B, 0x25B3, 0x25BD, 0x0338, 0x21A6,
0x2200, 0x2203, 0xAC, 0x2205, 0x211C, 0x22A9, 0x22A4, 0x22A5,
0x2135, 0x1D49C, 0x212C, 0x1D49E, 0x1D49F, 0x2130, 0x2131, 0x1D4A2,
0x210B, 0x2110, 0x1D4A5, 0x1D4A6, 0x2112, 0x2133, 0x1D4A9, 0x1D4AA,
0x1D4AB, 0x1D4AC, 0x211B, 0x1D4AE, 0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2,
0x1D4B3, 0x1D4B4, 0x1D4B5, 0x222A, 0x2229, 0x228E, 0x2227, 0x2228,
0x22A2, 0x22A3, 0x230A, 0x230B, 0x2308, 0x2309, 0x7B, 0x7D,
0x27E8, 0x27E9, 0x7C, 0x2016, 0x2195, 0x21D5, 0x5C, 0x2240,
0x221A, 0x2210, 0x2207, 0x222B, 0x2294, 0x2293, 0x2291, 0x2292,
0xA7, 0x2020, 0x2021, 0xB6, 0x2663, 0x2662, 0x2661, 0x2660,
}
return {
oml = remap_oml,
oms = remap_oms,
}

View File

@ -0,0 +1,99 @@
\ProvidesExplPackage {luamml-patches-amsmath} {2021-04-23} {0.0.1-alpha}
{Feel free to add a description here}
\lua_now:n { require'luamml-amsmath' }
\cs_set:Npn \align@preamble {
&
\hfil
\strut@
\setboxz@h {
\@lign
$
\m@th
\displaystyle {
##
}
\ifmeasuring@
\luamml_flag_ignore:
\else
\luamml_flag_alignment_left:
\fi
$
}
\ifmeasuring@
\savefieldlength@
\fi
\set@field
\tabskip\z@skip
&
\setboxz@h {
\@lign
$
\m@th
\displaystyle
{
{}
\luamml_last_math_alignmark:
##
}
\ifmeasuring@
\luamml_flag_ignore:
\else
\luamml_flag_alignment_right:
\fi
$
}
\ifmeasuring@
\savefieldlength@
\else
\luamml_amsmath_add_box_to_row:
\fi
\set@field
\hfil
\tabskip\alignsep@
}
\cs_set:Npn \math@cr@@@align {
\ifst@rred
\nonumber
\fi
\if@eqnsw
\global \tag@true
\fi
\global \advance \row@ \@ne
\add@amps \maxfields@
\omit
\kern -\alignsep@
\luamml_amsmath_finalize_row:
\iftag@
\setboxz@h {
\@lign
\strut@
{ \make@display@tag }
}
\place@tag
\fi
\ifst@rred
\else
\global \@eqnswtrue
\fi
\global \lineht@ \z@
\cr
}
\cs_set:Npn \endalign {
\math@cr
\black@ \totwidth@
\luamml_amsmath_finalize_table:
\egroup
\ifingather@
\restorealignstate@
\egroup
\nonumber
\ifnum0={\fi\iffalse}\fi
\else
$$
\fi
\ignorespacesafterend
}

75
luamml-tex.lua Normal file
View File

@ -0,0 +1,75 @@
local mlist_to_mml = require'luamml-convert'
local process_mlist = mlist_to_mml.process
local make_root = mlist_to_mml.make_root
local register_family = mlist_to_mml.register_family
local mappings = require'luamml-legacy-mappings'
local write_xml = require'luamml-xmlwriter'
local properties = node.get_properties_table()
local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
token.set_lua('RegisterFamilyMapping', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local fam = token.scan_int()
local mapping = token.scan_string()
if mappings[mapping] then
register_family(fam, mappings[mapping])
else
tex.error(string.format('Unknown font mapping %q', mapping))
end
end
-- Possible flag values:
-- 0: Normal (This is the only supported one in display mode)
-- 1: Like 0, result is display math
-- 2: Generate MathML, but only save it for later usage in startmath node
-- 3: Skip
-- 4: Prepend node list from buffer before generating
-- 5: Like 5, result is display math
-- 6: 2+4
-- 7: Skip but save copy of node list in buffer
--
-- In other words:
-- Bit 1: Suppress output
-- Bit 0: Force display if 1 isn't set, if it is then skip MathML generation
-- Bit 2: Integrate with table mechanism
local mlist_buffer
luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style)
local flag = tex.count.l__luamml_flag_int
if flag & 3 == 3 then
if flag & 4 == 4 then
assert(mlist_buffer == nil)
mlist_buffer = node.copy_list(mlist)
end
return true
end
local new_mlist, buffer_tail
if flag & 4 == 4 then
new_mlist, buffer_tail = assert(mlist_buffer), node.tail(mlist_buffer)
mlist.prev, buffer_tail.next = buffer_tail, mlist
mlist_buffer = nil
else
new_mlist = mlist
end
local xml = process_mlist(new_mlist, style == 'display' and 0 or 2)
if flag & 2 == 0 then
print(write_xml(make_root(xml, (style == 'display' or flag & 1 == 1) and 0 or 2)) .. '\n')
else
assert(style == 'text')
local startmath = tex.nest.top.tail
local props = properties[startmath]
if not props then
props = {}
properties[startmath] = props
end
props.saved_mathml_table = xml
end
if buffer_tail then
mlist.prev, buffer_tail.next = nil, nil
node.flush_list(new_mlist)
end
return true
end, 'dump_list')

View File

@ -3,7 +3,6 @@ local function escape_name(name)
return name return name
end end
-- FIXME: Not sure yet if this will be needed
local escapes = { local escapes = {
['"'] = "&quot;", ['"'] = "&quot;",
['<'] = "&lt;", ['<'] = "&lt;",
@ -41,4 +40,7 @@ local function write_elem(tree, indent)
return out .. '</' .. escaped_name .. '>' return out .. '</' .. escaped_name .. '>'
end end
return write_elem return function(element, indent, version)
return (version == '11' and '<?xml version="1.1"?>' or '') ..
write_elem(element, indent and '' or nil)
end

17
luamml.sty Normal file
View File

@ -0,0 +1,17 @@
\ProvidesExplPackage {luamml} {2021-04-23} {0.0.1-alpha}
{Feel free to add a description here}
\int_new:N \l__luamml_flag_int
\lua_now:n { require'luamml-tex' }
\cs_new:Nn \luamml_flag_ignore: {
\int_set:Nn \l__luamml_flag_int { 3 }
}
\cs_new:Nn \luamml_flag_alignment_left: {
\int_set:Nn \l__luamml_flag_int { 7 }
}
\cs_new:Nn \luamml_flag_alignment_right: {
\int_set:Nn \l__luamml_flag_int { 6 }
}
\RequirePackage { luamml-patches-amsmath }

View File

@ -1,14 +0,0 @@
local inspect = require'inspect'
local function show(t) return print(inspect(t)) end
local mlist_to_table = require'mlist_to_mml'
local write_xml = require'write_xml'
luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style)
print'\n\n'
local xml = mlist_to_table(mlist, style == 'display' and 2 or 0)
print(write_xml(xml))
-- print(write_xml(xml, '\n'))
print'\n'
return true
end, 'dump_list')

View File

@ -1,9 +1,12 @@
\documentclass{article} \documentclass{article}
\usepackage{unicode-math} \usepackage{unicode-math}
\directlua{require'test_tex'} \usepackage{amsmath}
\usepackage{luamml}
\RegisterFamilyMapping\symsymbols{oms}
\RegisterFamilyMapping\symletters{oml}
\begin{document} \begin{document}
\[ \[
ax^2+b+c=0 ax^2+bx+c=0
\] \]
\[ \[
x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}. x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}.
@ -12,5 +15,11 @@
\sum_a\underline c\dot bc' \sum_a\underline c\dot bc'
\] \]
\begin{align}
abc&=def\\
1+2&=3\\
5
\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0$. Es gilt $\sin(x)-\sin(x+2\pi)=0$.
\end{document} \end{document}

View File

@ -1,16 +0,0 @@
local write_xml = require'write_xml'
print(write_xml{[0] = "math", xmlns = "http://www.w3.org/1998/Math/MathML",
{[0] = "mi", "a"},
{[0] = "msup",
{[0] = "mi", "x"},
{[0] = "mn", "2"},
},
{[0] = "mo", "+"},
{[0] = "mi", "b"},
{[0] = "mi", "x"},
{[0] = "mo", "+"},
{[0] = "mi", "c"},
{[0] = "mo", "="},
{[0] = "mn", "0"},
})