From 8be2760919141ea5b8f38bf046193fc81383ef53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Thu, 22 Apr 2021 23:38:28 +0200 Subject: [PATCH 1/4] Don't require unicode-math --- mlist_to_mml.lua | 41 ++++++++++++++++++++++++++++++++++------- test_tex.lua | 24 ++++++++++++++++++++---- test_tex.tex | 4 +++- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/mlist_to_mml.lua b/mlist_to_mml.lua index 67ee947..a1ba373 100644 --- a/mlist_to_mml.lua +++ b/mlist_to_mml.lua @@ -14,6 +14,16 @@ local noad_sub = node.subtypes'noad' local radical_sub = node.subtypes'radical' 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,} + local nodes_to_table local function sub_style(s) return s//4*2+5 end @@ -25,7 +35,8 @@ local function delim_to_table(delim) local props = properties[delim] props = props and props.mathml_table if props then return props end 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 -- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars @@ -36,9 +47,9 @@ local function acc_to_table(acc, cur_style, stretch) if acc.id ~= math_char_t then error'confusion' end - local char = utf8.char(acc.char) - char = remap_comb[char] or char 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 stretch = nil end @@ -51,10 +62,14 @@ local function kernel_to_table(kernel, cur_style) if props then return props end local id = kernel.id 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 - 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' + return {[0] = elem, + char, + ['tex:family'] = fam ~= 0 and fam or nil, + mathvariant = #char == 1 and utf8.codepoint(char) < 0x10000 and 'normal' or nil + } elseif id == sub_box_t then return {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list}} elseif id == sub_mlist_t then @@ -269,7 +284,14 @@ function nodes_to_table(head, cur_style) return result end -return function(head, style) +local function register_remap(family, mapping) + family = family << 21 + for from, to in next, mapping do + remap_lookup[family | from] = utf8.char(to) + end +end + +local function to_mml(head, style) local result = nodes_to_table(head, style or 0) result[0] = 'math' result.xmlns = 'http://www.w3.org/1998/Math/MathML' @@ -279,3 +301,8 @@ return function(head, style) end return result end + +return { + register_family = register_remap, + process = to_mml, +} diff --git a/test_tex.lua b/test_tex.lua index 50b1b1a..697172b 100644 --- a/test_tex.lua +++ b/test_tex.lua @@ -1,14 +1,30 @@ local inspect = require'inspect' local function show(t) return print(inspect(t)) end -local mlist_to_table = require'mlist_to_mml' +local mlist_to_mml = require'mlist_to_mml' +local process_mlist = mlist_to_mml.process +local register_family = mlist_to_mml.register_family + +local mappings = require'remap' local write_xml = require'write_xml' +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 + 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'' + local xml = process_mlist(mlist, style == 'display' and 2 or 0) print(write_xml(xml)) -- print(write_xml(xml, '\n')) - print'\n' + print'' return true end, 'dump_list') diff --git a/test_tex.tex b/test_tex.tex index 73d8317..bd945a2 100644 --- a/test_tex.tex +++ b/test_tex.tex @@ -1,6 +1,8 @@ \documentclass{article} -\usepackage{unicode-math} +% \usepackage{unicode-math} \directlua{require'test_tex'} +\RegisterFamilyMapping\symsymbols{oms} +\RegisterFamilyMapping\symletters{oml} \begin{document} \[ ax^2+b+c=0 From d6bda3aff3799d8943c1a49a162dd2d9771835c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Fri, 23 Apr 2021 02:58:07 +0200 Subject: [PATCH 2/4] Reorganize and add missing file --- mlist_to_mml.lua => luamml-convert.lua | 4 +- remap_comb.lua => luamml-data-combining.lua | 0 stretchy.lua => luamml-data-stretchy.lua | 0 luamml-legacy-mappings.lua | 51 +++++++++++++++++++++ test_tex.lua => luamml-tex.lua | 9 ++-- write_xml.lua => luamml-xmlwriter.lua | 6 ++- test_tex.tex | 2 +- test_xml.lua | 16 ------- 8 files changed, 61 insertions(+), 27 deletions(-) rename mlist_to_mml.lua => luamml-convert.lua (99%) rename remap_comb.lua => luamml-data-combining.lua (100%) rename stretchy.lua => luamml-data-stretchy.lua (100%) create mode 100644 luamml-legacy-mappings.lua rename test_tex.lua => luamml-tex.lua (79%) rename write_xml.lua => luamml-xmlwriter.lua (87%) delete mode 100644 test_xml.lua diff --git a/mlist_to_mml.lua b/luamml-convert.lua similarity index 99% rename from mlist_to_mml.lua rename to luamml-convert.lua index a1ba373..3451628 100644 --- a/mlist_to_mml.lua +++ b/luamml-convert.lua @@ -1,5 +1,5 @@ -local remap_comb = require'remap_comb' -local stretchy = require'stretchy' +local remap_comb = require'luamml-data-combining' +local stretchy = require'luamml-data-stretchy' local properties = node.get_properties_table() diff --git a/remap_comb.lua b/luamml-data-combining.lua similarity index 100% rename from remap_comb.lua rename to luamml-data-combining.lua diff --git a/stretchy.lua b/luamml-data-stretchy.lua similarity index 100% rename from stretchy.lua rename to luamml-data-stretchy.lua diff --git a/luamml-legacy-mappings.lua b/luamml-legacy-mappings.lua new file mode 100644 index 0000000..c969a8d --- /dev/null +++ b/luamml-legacy-mappings.lua @@ -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, +} diff --git a/test_tex.lua b/luamml-tex.lua similarity index 79% rename from test_tex.lua rename to luamml-tex.lua index 697172b..6fbf975 100644 --- a/test_tex.lua +++ b/luamml-tex.lua @@ -1,12 +1,9 @@ -local inspect = require'inspect' -local function show(t) return print(inspect(t)) end - -local mlist_to_mml = require'mlist_to_mml' +local mlist_to_mml = require'luamml-convert' local process_mlist = mlist_to_mml.process local register_family = mlist_to_mml.register_family -local mappings = require'remap' -local write_xml = require'write_xml' +local mappings = require'luamml-legacy-mappings' +local write_xml = require'luamml-xmlwriter' local funcid = luatexbase.new_luafunction'RegisterFamilyMapping' token.set_lua('RegisterFamilyMapping', funcid, 'protected') diff --git a/write_xml.lua b/luamml-xmlwriter.lua similarity index 87% rename from write_xml.lua rename to luamml-xmlwriter.lua index f67db4e..98587ff 100644 --- a/write_xml.lua +++ b/luamml-xmlwriter.lua @@ -3,7 +3,6 @@ local function escape_name(name) return name end --- FIXME: Not sure yet if this will be needed local escapes = { ['"'] = """, ['<'] = "<", @@ -41,4 +40,7 @@ local function write_elem(tree, indent) return out .. '' end -return write_elem +return function(element, indent, version) + return (version == '11' and '' or '') .. + write_elem(element, indent and '' or nil) +end diff --git a/test_tex.tex b/test_tex.tex index bd945a2..af6dd48 100644 --- a/test_tex.tex +++ b/test_tex.tex @@ -1,6 +1,6 @@ \documentclass{article} % \usepackage{unicode-math} -\directlua{require'test_tex'} +\directlua{require'luamml-tex'} \RegisterFamilyMapping\symsymbols{oms} \RegisterFamilyMapping\symletters{oml} \begin{document} diff --git a/test_xml.lua b/test_xml.lua deleted file mode 100644 index f513cb1..0000000 --- a/test_xml.lua +++ /dev/null @@ -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"}, - }) From c0a1f60962113e39923be6efccaaf00da110833e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Fri, 23 Apr 2021 07:50:21 +0200 Subject: [PATCH 3/4] Better embellished operator handling --- luamml-convert.lua | 109 +++++++++++++++++++++++++++------------------ luamml-tex.lua | 1 - 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/luamml-convert.lua b/luamml-convert.lua index 3451628..c1d8458 100644 --- a/luamml-convert.lua +++ b/luamml-convert.lua @@ -24,11 +24,20 @@ local digit_map = {["0"] = true, ["1"] = 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 function sub_style(s) return s//4*2+5 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 ) 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 local function delim_to_table(delim) if not delim then return end @@ -59,19 +68,21 @@ end local function kernel_to_table(kernel, cur_style) if not kernel then return end 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 if id == math_char_t then local fam = kernel.fam local char = remap_lookup[fam << 21 | kernel.char] local elem = digit_map[char] and 'mn' or 'mi' - return {[0] = elem, + local result = {[0] = elem, char, ['tex:family'] = fam ~= 0 and fam or nil, mathvariant = #char == 1 and utf8.codepoint(char) < 0x10000 and 'normal' or nil } + return result, result 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 return nodes_to_table(kernel.list, cur_style) else @@ -79,34 +90,34 @@ local function kernel_to_table(kernel, cur_style) 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 sup = kernel_to_table(n.sup, sup_style(cur_style)) if sub then if sup then - return {[0] = 'msubsup', t, sub, sup} + return {[0] = 'msubsup', t, sub, sup}, core else - return {[0] = 'msub', t, sub} + return {[0] = 'msub', t, sub}, core end elseif sup then - return {[0] = 'msup', t, sup} + return {[0] = 'msup', t, sup}, core else - return t + return t, core end end local function noad_to_table(noad, sub, cur_style) 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 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 - if nucleus[0] == 'mrow' then + if not core or not core[0] then -- TODO else - nucleus[0] = 'mo' - if stretchy[nucleus[1]] then nucleus.stretchy = false end - if nucleus.mathvariant == 'normal' then nucleus.mathvariant = nil end + core[0] = 'mo' + if stretchy[core[1]] then core.stretchy = false end + if core.mathvariant == 'normal' then core.mathvariant = nil end end nucleus['tex:class'] = class @@ -118,35 +129,35 @@ local function noad_to_table(noad, sub, cur_style) nucleus, sub or sup, sub and sup, - } + }, core end elseif class == 'under' then return {[0] = 'munder', nucleus, {[0] = 'mo', '_',}, - } + }, core elseif class == 'over' then return {[0] = 'mover', nucleus, {[0] = 'mo', '\u{203E}',}, - } + }, core elseif class == 'vcenter' then nucleus['tex:TODO'] = class else error[[confusion]] end - return do_sub_sup(nucleus, noad, cur_style) + return do_sub_sup(nucleus, core, noad, cur_style) end 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 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', nucleus, bot_acc or top_acc, bot_acc and top_acc, - } + }, core end local style_table = { @@ -163,19 +174,19 @@ style_table.crampedscript, style_table.crampedscriptscript = local function radical_to_table(radical, sub, cur_style) 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 elem if kind == 'radical' or kind == 'uradical' then -- FIXME: Check that this is really a square root - elem = {[0] = 'msqrt', nucleus} + elem, core = {[0] = 'msqrt', nucleus}, nil elseif kind == 'uroot' then -- 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 - elem = {[0] = 'munder', left, nucleus} + elem, core = {[0] = 'munder', left, nucleus}, left elseif kind == 'uoverdelimiter' then - elem = {[0] = 'mover', left, nucleus} + elem, core = {[0] = 'mover', left, nucleus}, left elseif kind == 'udelimiterunder' then elem = {[0] = 'munder', nucleus, left} elseif kind == 'udelimiterover' then @@ -183,11 +194,11 @@ local function radical_to_table(radical, sub, cur_style) else error[[confusion]] end - return do_sub_sup(elem, radical, cur_style) + return do_sub_sup(elem, core, radical, cur_style) end 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 left = delim_to_table(fraction.left) local right = delim_to_table(fraction.right) @@ -209,23 +220,24 @@ local function fraction_to_table(fraction, sub, cur_style) right, } else - return mfrac + return mfrac, core end end local function fence_to_table(fence, sub, cur_style) local delim = delim_to_table(fence.delimiter) delim.fence = 'true' - return delim + return delim, delim end local function space_to_table(amount, sub, cur_style) 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 - 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 - 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 @@ -233,14 +245,16 @@ function nodes_to_table(head, cur_style) local t = {[0] = "mrow"} local result = t local nonscript + local core = space_like for n, id, sub in node.traverse(head) do + local new_core local props = properties[n] props = props and props.mathml_table if props then - t[#t+1] = props + t[#t+1], new_core = props, user_provided 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 - 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 if #t ~= 0 then local new_t = {[0] = 'mstyle'} @@ -253,35 +267,42 @@ function nodes_to_table(head, cur_style) t.displaystyle, t.scriptlevel = false, sub//2 - 1 end cur_style = sub + new_core = space_like elseif id == choice_t then local size = cur_style//2 - t[#t+1] = nodes_to_table(n[size == 0 and 'display' or size == 1 and 'text' - or size == 2 and 'script' - or size == 3 and 'scriptscript' or assert(false)], 2*size) + t[#t+1], new_core = nodes_to_table(n[size == 0 and 'display' + or size == 1 and 'text' + or size == 2 and 'script' + or size == 3 and 'scriptscript' + or assert(false)], 2*size), space_like 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 - 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 - 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 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 elseif id == glue_t then if cur_style >= 4 or not nonscript then if sub == 98 then -- TODO magic number nonscript = true 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 else - t[#t+1] = {[0] = 'tex:TODO', other = n} + new_core = {[0] = 'tex:TODO', other = n} + t[#t+1] = new_core end nonscript = nil + if core and new_core ~= space_like then + core = new_core + end end - return result + return result, core end local function register_remap(family, mapping) diff --git a/luamml-tex.lua b/luamml-tex.lua index 6fbf975..3b77139 100644 --- a/luamml-tex.lua +++ b/luamml-tex.lua @@ -21,7 +21,6 @@ luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style) print'' local xml = process_mlist(mlist, style == 'display' and 2 or 0) print(write_xml(xml)) - -- print(write_xml(xml, '\n')) print'' return true end, 'dump_list') From 23c6714e97354255991075f89cb8e5010f405c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Sat, 24 Apr 2021 16:53:50 +0200 Subject: [PATCH 4/4] align support and fixes --- luamml-amsmath.lua | 84 ++++++++++++++++++++++++++++++++ luamml-convert.lua | 48 ++++++++++-------- luamml-patches-amsmath.sty | 99 ++++++++++++++++++++++++++++++++++++++ luamml-tex.lua | 57 ++++++++++++++++++++-- luamml.sty | 17 +++++++ test_tex.tex | 13 +++-- 6 files changed, 291 insertions(+), 27 deletions(-) create mode 100644 luamml-amsmath.lua create mode 100644 luamml-patches-amsmath.sty create mode 100644 luamml.sty diff --git a/luamml-amsmath.lua b/luamml-amsmath.lua new file mode 100644 index 0000000..c03fb35 --- /dev/null +++ b/luamml-amsmath.lua @@ -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 diff --git a/luamml-convert.lua b/luamml-convert.lua index c1d8458..c7b312c 100644 --- a/luamml-convert.lua +++ b/luamml-convert.lua @@ -77,7 +77,7 @@ local function kernel_to_table(kernel, cur_style) local result = {[0] = elem, char, ['tex:family'] = fam ~= 0 and fam or nil, - mathvariant = #char == 1 and utf8.codepoint(char) < 0x10000 and 'normal' 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 @@ -256,17 +256,21 @@ function nodes_to_table(head, cur_style) elseif id == accent_t then t[#t+1], new_core = accent_to_table(n, sub, cur_style) elseif id == style_t then - if #t ~= 0 then - local new_t = {[0] = 'mstyle'} - t[#t+1] = new_t - t = new_t + if sub ~= cur_style then + if #t == 0 then + t[0] = 'mstyle' + 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 - if sub < 2 then - t.displaystyle, t.scriptlevel = true, 0 - else - t.displaystyle, t.scriptlevel = false, sub//2 - 1 - end - cur_style = sub new_core = space_like elseif id == choice_t then local size = cur_style//2 @@ -312,18 +316,22 @@ local function register_remap(family, mapping) end end -local function to_mml(head, style) - local result = nodes_to_table(head, style or 0) - result[0] = 'math' - result.xmlns = 'http://www.w3.org/1998/Math/MathML' - result['xmlns:tex'] = 'http://typesetting.eu/2021/LuaMathML' - if style == 2 then - result.display = 'block' +local function to_math(root, style) + if root[0] == 'mrow' then + root[0] = 'math' + else + root = {[0] = 'math', root} end - return result + 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 = to_mml, + process = function(head, style) return nodes_to_table(head, style or 2) end, + make_root = to_math, } diff --git a/luamml-patches-amsmath.sty b/luamml-patches-amsmath.sty new file mode 100644 index 0000000..15565fe --- /dev/null +++ b/luamml-patches-amsmath.sty @@ -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 +} diff --git a/luamml-tex.lua b/luamml-tex.lua index 3b77139..ad9c176 100644 --- a/luamml-tex.lua +++ b/luamml-tex.lua @@ -1,10 +1,13 @@ 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() @@ -17,10 +20,56 @@ lua.get_functions_table()[funcid] = function() 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) - print'' - local xml = process_mlist(mlist, style == 'display' and 2 or 0) - print(write_xml(xml)) - print'' + 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') diff --git a/luamml.sty b/luamml.sty new file mode 100644 index 0000000..f95c505 --- /dev/null +++ b/luamml.sty @@ -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 } diff --git a/test_tex.tex b/test_tex.tex index af6dd48..b4bc7ca 100644 --- a/test_tex.tex +++ b/test_tex.tex @@ -1,11 +1,12 @@ \documentclass{article} -% \usepackage{unicode-math} -\directlua{require'luamml-tex'} +\usepackage{unicode-math} +\usepackage{amsmath} +\usepackage{luamml} \RegisterFamilyMapping\symsymbols{oms} \RegisterFamilyMapping\symletters{oml} \begin{document} \[ - ax^2+b+c=0 + ax^2+bx+c=0 \] \[ x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}. @@ -14,5 +15,11 @@ \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$. \end{document}