From c4c2b701b5524c9c1326c4512cd7aee24f41d635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Fabian=20Kr=C3=BCger?= Date: Wed, 12 May 2021 07:43:57 +0200 Subject: [PATCH] First attempt at generating structure elements --- luamml-convert.lua | 12 +++--- luamml-structelemwriter.lua | 83 +++++++++++++++++++++++++++++++++++++ luamml-tex.lua | 4 ++ luamml-xmlwriter.lua | 2 +- luamml.sty | 3 ++ test_tex.tex | 1 + 6 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 luamml-structelemwriter.lua diff --git a/luamml-convert.lua b/luamml-convert.lua index 2d6600b..0676ad7 100644 --- a/luamml-convert.lua +++ b/luamml-convert.lua @@ -129,7 +129,7 @@ local function delim_to_table(delim) else local fam = delim.small_fam char = remap_lookup[fam << 21 | char] - local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil, lspace = 0, rspace = 0 } + local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil, lspace = 0, rspace = 0, [':node'] = delim } return result, result end end @@ -149,7 +149,7 @@ local function acc_to_table(acc, cur_style, stretch) if stretch ~= not stretchy[char] then -- Handle nil gracefully in stretchy stretch = nil end - return {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = stretch} + return {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = stretch, [':node'] = acc} end local function kernel_to_table(kernel, cur_style) @@ -164,7 +164,8 @@ local function kernel_to_table(kernel, cur_style) local result = {[0] = elem, char, ['tex:family'] = fam ~= 0 and fam or nil, - mathvariant = utf8.len(char) == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil + mathvariant = utf8.len(char) == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil, + [':node'] = kernel, } return result, result elseif id == sub_box_t then @@ -172,7 +173,7 @@ local function kernel_to_table(kernel, cur_style) local result = to_text(kernel.list.head) return result, result else - local result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list}} + local result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list, [':node'] = kernel}} return result, result end elseif id == sub_mlist_t then @@ -305,7 +306,7 @@ local function radical_to_table(radical, sub, cur_style) local elem if kind == 'radical' or kind == 'uradical' then -- FIXME: Check that this is really a square root - elem, core = {[0] = 'msqrt', nucleus}, nil + elem, core = {[0] = 'msqrt', nucleus, [':node'] = left[':node'], }, nil elseif kind == 'uroot' then -- FIXME: Check that this is really a root elem, core = {[0] = 'msqrt', nucleus, kernel_to_table(radical.degree)}, nil @@ -331,6 +332,7 @@ local function fraction_to_table(fraction, sub, cur_style) local mfrac = {[0] = 'mfrac', linethickness = fraction.width and fraction.width == 0 and 0 or nil, bevelled = fraction.middle and "true" or nil, + [':node'] = fraction, num, denom, } diff --git a/luamml-structelemwriter.lua b/luamml-structelemwriter.lua new file mode 100644 index 0000000..31afb0c --- /dev/null +++ b/luamml-structelemwriter.lua @@ -0,0 +1,83 @@ +local struct_begin = token.create'tag_struct_begin:n' +local struct_end = token.create'tag_struct_end:' + +local mc_begin = token.create'tag_mc_begin:n' +local mc_end = token.create'tag_mc_end:' + +local function escape_name(name) + return name +end + +local function escape_string(str) + return str +end + +local mathml_ns_obj +local function get_mathml_ns_obj() + mathml_ns_obj = mathml_ns_obj or token.create'c__pdf_backend_object_tag/NS/mathml_int'.index + return mathml_ns_obj +end + +local attribute_counter = 0 +local attributes = setmetatable({}, {__index = function(t, k) + attribute_counter = attribute_counter + 1 + local attr_name = string.format('luamml_attr_%i', attribute_counter) + t[k] = attr_name + tex.runtoks(function() + -- tex.sprint(string.format('\\tagpdfsetup{newattribute={%s}{/O/NSO/NS %i 0 R', + -- attr_name, mathml_ns_obj or get_mathml_ns_obj())) + tex.sprint(string.format('\\tagpdfsetup{newattribute={%s}{/O/MathML-3', + attr_name)) + tex.cprint(12, k) + tex.sprint'}}' + end) + return attr_name +end}) + +local mc_type = token.create'l__tag_mc_type_attr'.index +local mc_cnt = token.create'l__tag_mc_cnt_attr'.index +-- print('!!!', mc_type, mc_cnt) + +local attrs = {} +local function write_elem(tree, indent) + if not tree[0] then print('ERR', require'inspect'(tree)) end + local i = 0 + for attr, val in next, tree do if type(attr) == 'string' and not string.find(attr, ':') and attr ~= 'xmlns' then + -- for attr, val in next, tree do if type(attr) == 'string' and string.byte(attr) ~= 0x3A then + i = i + 1 + attrs[i] = string.format('/%s(%s)', escape_name(attr), escape_string(val)) + end end + table.sort(attrs) + local attr_name + if i == 0 then + tex.sprint{struct_begin, string.format('{tag=%s/mathml}', tree[0])} + else + tex.sprint{struct_begin, string.format('{tag=%s/mathml,attribute=%s}', tree[0], attributes[table.concat(attrs)])} + end + for j = 1, i do attrs[j] = nil end + + if tree[':node'] then + local n = tree[':node'] + tex.runtoks(function() + tex.sprint{mc_begin, string.format('{tag=%s}', tree[0])} + -- NOTE: This will also flush all previous sprint's... That's often annoying, but in this case actually intentional. + end) + node.set_attribute(tree[':node'], mc_type, tex.attribute[mc_type]) + node.set_attribute(tree[':node'], mc_cnt, tex.attribute[mc_cnt]) + tex.runtoks(function() + tex.sprint(mc_end) + end) + end + for _, elem in ipairs(tree) do + if type(elem) ~= 'string' then + write_elem(elem) + end + end + tex.runtoks(function() + tex.sprint(struct_end) + end) +end + +return function(element) + return write_elem(element) +end diff --git a/luamml-tex.lua b/luamml-tex.lua index 36edff8..852c93a 100644 --- a/luamml-tex.lua +++ b/luamml-tex.lua @@ -5,6 +5,7 @@ local register_family = mlist_to_mml.register_family local mappings = require'luamml-legacy-mappings' local write_xml = require'luamml-xmlwriter' +local write_struct = require'luamml-structelemwriter' local properties = node.get_properties_table() @@ -67,6 +68,9 @@ luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style) if flag & 2 == 0 then save_result(xml, style == 'display' or flag & 1 == 1) end + if flag & 8 == 8 then + write_struct(make_root(mlist_result, mlist_display and 0 or 2)) + end if style == 'text' then local startmath = tex.nest.top.tail local props = properties[startmath] diff --git a/luamml-xmlwriter.lua b/luamml-xmlwriter.lua index efd5f4f..4f06842 100644 --- a/luamml-xmlwriter.lua +++ b/luamml-xmlwriter.lua @@ -18,7 +18,7 @@ local function write_elem(tree, indent) if not tree[0] then print('ERR', require'inspect'(tree)) end local escaped_name = escape_name(assert(tree[0])) local i = 0 - for attr, val in next, tree do if type(attr) == 'string' then + for attr, val in next, tree do if type(attr) == 'string' and string.byte(attr) ~= 0x3A then i = i + 1 attrs[i] = string.format(' %s="%s"', escape_name(attr), escape_text(val)) end end diff --git a/luamml.sty b/luamml.sty index f563a75..10e07ec 100644 --- a/luamml.sty +++ b/luamml.sty @@ -17,6 +17,9 @@ \cs_new:Nn \luamml_flag_alignment_right: { \int_set:Nn \l__luamml_flag_int { 6 } } +\cs_new:Nn \luamml_flag_structelem: { + \int_set:Nn \l__luamml_flag_int { 8 } +} \cs_new:Npn \__luamml_patch_package:nn #1 #2 { \@ifpackageloaded {#1} {#2} { diff --git a/test_tex.tex b/test_tex.tex index 55c341a..eb305dd 100644 --- a/test_tex.tex +++ b/test_tex.tex @@ -29,6 +29,7 @@ 2 & $else$ \end{cases} \] +\ShowMathMLObj \[ x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}. \]