mathml/luamml-tex.lua

233 lines
7.3 KiB
Lua
Raw Normal View History

2021-04-23 02:58:07 +02:00
local mlist_to_mml = require'luamml-convert'
2021-04-22 23:38:28 +02:00
local process_mlist = mlist_to_mml.process
2021-04-24 16:53:50 +02:00
local make_root = mlist_to_mml.make_root
2021-04-22 23:38:28 +02:00
local register_family = mlist_to_mml.register_family
2021-04-23 02:58:07 +02:00
local mappings = require'luamml-legacy-mappings'
local write_xml = require'luamml-xmlwriter'
local write_struct = require'luamml-structelemwriter'
2021-04-18 15:19:52 +02:00
local filename_token = token.create'l__luamml_filename_tl'
2021-06-27 00:43:30 +02:00
local label_token = token.create'l__luamml_label_tl'
local left_brace = token.new(string.byte'{', 1)
local right_brace = token.new(string.byte'}', 2)
local output_hook_token
2023-12-27 15:32:27 +01:00
local global_text_families = {}
local text_families_meta = {__index = function(t, fam)
2023-12-27 15:32:27 +01:00
if fam == nil then return nil end
local assignment = global_text_families[fam]
if assignment == nil then
local fid = node.family_font(fam)
local fontdir = font.getfont(fid)
if not fontdir then
-- FIXME(?): If there is no font...
error'Please load your fonts?!?'
end
2023-12-27 18:14:46 +01:00
assignment = not (fontdir.MathConstants and next(fontdir.MathConstants))
2023-12-27 15:32:27 +01:00
end
t[fam] = assignment
return assignment
end}
2021-04-24 16:53:50 +02:00
local properties = node.get_properties_table()
2021-06-22 12:56:04 +02:00
local mmode, hmode, vmode do
local result, input = {}, tex.getmodevalues()
for k,v in next, tex.getmodevalues() do
if v == 'math' then mmode = k
elseif v == 'horizontal' then hmode = k
elseif v == 'vertical' then vmode = k
else assert(v == 'unset')
end
end
assert(mmode and hmode and vmode)
end
2021-04-24 16:53:50 +02:00
2021-04-22 23:38:28 +02:00
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])
2023-12-27 18:14:46 +01:00
if global_text_families[fam] == nil then
global_text_families[fam] = false
end
2021-04-22 23:38:28 +02:00
else
tex.error(string.format('Unknown font mapping %q', mapping))
end
end
2021-06-06 06:15:57 +02:00
local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
token.set_lua('RegisterTextFamily', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local fam = token.scan_int()
2023-12-27 13:30:40 +01:00
local _kind = token.scan_string()
2023-12-27 15:32:27 +01:00
global_text_families[fam] = true
2021-06-06 06:15:57 +02:00
end
2021-05-21 17:30:23 +02:00
local function shallow_copy(t)
local tt = {}
for k,v in next, t do
tt[k] = v
end
return tt
end
2021-04-24 16:53:50 +02:00
-- Possible flag values:
2021-06-16 15:19:26 +02:00
-- 0: Skip
-- 1: Generate MathML, but only save it for later usage in startmath node
-- 3: Normal (This is the only supported one in display mode)
-- 11: Generate MathML structure elements
2021-04-24 16:53:50 +02:00
--
2021-06-16 15:19:26 +02:00
-- More generally, flags is a bitfield with the defined bits:
-- Bit 5-7: See Bit 4
-- Bit 4: Overwrite mathstyle with bit 9-11
-- Bit 3: Generate MathML structure elements
2021-06-26 22:38:24 +02:00
-- Bit 2: Change root element name for saved element
2021-06-16 15:19:26 +02:00
-- Bit 1: Save MathML as a fully converted formula
-- Bit 0: Save MathML for later usage in startmath node. Ignored for display math.
2021-04-24 16:53:50 +02:00
2021-10-31 17:18:23 +01:00
local out_file
2021-06-16 15:19:26 +02:00
local mlist_result
2021-04-24 16:53:50 +02:00
local undefined_cmd = token.command_id'undefined_cs'
local call_cmd = token.command_id'call'
2021-06-27 00:43:30 +02:00
local labelled_mathml = {}
2021-06-16 15:19:26 +02:00
local function save_result(xml, display, structelem)
mlist_result = make_root(xml, display and 0 or 2)
2021-10-31 17:18:23 +01:00
if out_file then
out_file:write(write_xml(mlist_result, true):sub(2) .. '\n')
else
token.put_next(filename_token)
local filename = token.scan_argument()
if filename ~= '' then
assert(io.open(filename, 'w'))
:write(write_xml(mlist_result, true):sub(2) .. '\n')
:close()
end
end
2021-10-31 17:18:23 +01:00
local tracing = tex.count.tracingmathml > 1
2021-05-21 17:30:23 +02:00
if tracing then
2021-06-16 15:19:26 +02:00
texio.write_nl(write_xml(mlist_result) .. '\n')
2021-04-27 00:07:17 +02:00
end
if output_hook_token then
tex.runtoks(function()
tex.sprint(-2, output_hook_token, left_brace, write_xml(mlist_result), right_brace)
end)
end
2021-06-23 14:35:43 +02:00
if tex.count.l__luamml_flag_int & 8 == 8 then
write_struct(mlist_result)
end
2021-06-16 15:19:26 +02:00
return mlist_result
2021-04-25 18:09:13 +02:00
end
2021-04-19 13:30:54 +02:00
luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style)
2021-06-22 12:56:04 +02:00
if tex.nest.top.mode == mmode then -- This is a equation label generated with \eqno
return true
end
2021-04-24 16:53:50 +02:00
local flag = tex.count.l__luamml_flag_int
2021-06-16 15:19:26 +02:00
if flag & 3 == 0 then
2021-04-24 16:53:50 +02:00
return true
end
2021-06-16 15:19:26 +02:00
local display = style == 'display'
2021-06-23 14:35:43 +02:00
local startmath = tex.nest.top.tail -- Must come before any write_struct calls which adds nodes
2021-06-28 06:35:57 +02:00
style = flag & 16 == 16 and flag>>5 & 0x7 or display and 0 or 2
local xml, core = process_mlist(mlist, style, setmetatable({}, text_families_meta))
2021-06-16 15:19:26 +02:00
if flag & 2 == 2 then
2021-06-23 18:49:13 +02:00
xml = save_result(shallow_copy(xml), display)
2021-06-26 22:38:24 +02:00
end
if flag & 4 == 4 then
2021-06-16 15:19:26 +02:00
local element_type = token.get_macro'l__luamml_root_tl'
if element_type ~= 'mrow' then
if xml[0] == 'mrow' then
xml[0] = element_type
else
xml = {[0] = element_type, xml}
end
end
end
2021-06-16 15:19:26 +02:00
if not display and flag & 1 == 1 then
2021-04-24 16:53:50 +02:00
local props = properties[startmath]
if not props then
props = {}
properties[startmath] = props
end
2021-05-31 01:54:21 +02:00
props.saved_mathml_table, props.saved_mathml_core = xml, core
2021-06-27 00:43:30 +02:00
token.put_next(label_token)
local label = token.scan_argument()
if label ~= '' then
if labelled_mathml[label] then
tex.error('MathML Label already in use', {
'A MathML expression has a label which is already used by another \z
formula. If you do not want to label this formula with a unique \z
label, set a empty label instead.'})
else
labelled_mathml[label] = xml
end
end
2021-06-23 14:35:43 +02:00
if flag & 10 == 8 then
write_struct(xml, true) -- This modifies xml in-place to reference the struture element
end
2021-06-16 15:19:26 +02:00
end
2021-04-18 15:19:52 +02:00
return true
end, 'dump_list')
funcid = luatexbase.new_luafunction'luamml_get_last_mathml_stream:e'
token.set_lua('luamml_get_last_mathml_stream:e', funcid)
lua.get_functions_table()[funcid] = function()
if not mlist_result then
tex.error('No current MathML data', {
"I was asked to provide MathML code for the last formula, but there weren't any new formulas since you last asked."
})
end
2021-06-16 15:19:26 +02:00
local mml = write_xml(mlist_result)
2021-04-27 00:07:17 +02:00
if tex.count.tracingmathml == 1 then
texio.write_nl(mml .. '\n')
2021-04-27 00:07:17 +02:00
end
tex.sprint(-2, tostring(pdf.immediateobj('stream', mml, '/Subtype/application#2Fmathml+xml' .. token.scan_argument(true))))
mlist_result = nil
end
2021-04-25 18:09:13 +02:00
2021-10-31 17:18:23 +01:00
funcid = luatexbase.new_luafunction'luamml_begin_single_file:'
token.set_lua('luamml_begin_single_file:', funcid, 'protected')
2021-10-31 17:18:23 +01:00
lua.get_functions_table()[funcid] = function()
token.put_next(filename_token)
local filename = token.scan_argument()
if filename ~= '' then
out_file = assert(io.open(filename, 'w'))
end
end
funcid = luatexbase.new_luafunction'luamml_end_single_file:'
token.set_lua('luamml_end_single_file:', funcid, 'protected')
2021-10-31 17:18:23 +01:00
lua.get_functions_table()[funcid] = function()
if out_file then
out_file:close()
out_file = nil
end
end
funcid = luatexbase.new_luafunction'luamml_register_output_hook:N'
token.set_lua('__luamml_register_output_hook:N', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
output_hook_token = token.get_next()
end
funcid = luatexbase.new_luafunction'luamml_disable_output_hook:'
token.set_lua('__luamml_disable_output_hook:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
output_hook_token = nil
end
local annotate_context = require'luamml-tex-annotate'
annotate_context.data.mathml = labelled_mathml
2021-06-16 15:16:14 +02:00
2021-04-25 18:09:13 +02:00
return {
save_result = save_result,
2021-06-27 00:43:30 +02:00
labelled = labelled_mathml,
2021-04-25 18:09:13 +02:00
}