diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/optex/base/optex.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/optex/base/optex.lua | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/luatex/optex/base/optex.lua b/Master/texmf-dist/tex/luatex/optex/base/optex.lua new file mode 100644 index 00000000000..8201b8b3061 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/optex/base/optex.lua @@ -0,0 +1,351 @@ +-- This is part of OpTeX project, see http://petr.olsak.net/optex + +-- The basic lua functions and declarations used in \OpTeX/ are here + +-- GENERAL + +-- Error function used by following functions for critical errors. +local function err(message) + error("\nerror: "..message.."\n") +end +-- +-- For a `\chardef`'d, `\countdef`'d, etc., csname return corresponding register +-- number. The responsibility of providing a `\XXdef`'d name is on the caller. +function registernumber(name) + return token.create(name).index +end +-- +-- ALLOCATORS +alloc = alloc or {} +-- +-- An attribute allocator in Lua that cooperates with normal \OpTeX/ allocator. +local attributes = {} +local attribute_max = registernumber("_maiattribute") +function alloc.new_attribute(name) + local cnt = tex.count["_attributealloc"] + 1 + if cnt > attribute_max then + tex.error("No room for a new attribute") + else + tex.setcount("global", "_attributealloc", cnt) + texio.write_nl("log", '"'..name..'"=\\attribute'..tostring(cnt)) + attributes[name] = cnt + return cnt + end +end +-- +-- CALLBACKS +callback = callback or {} +-- +-- Save `callback.register` function for internal use. +local callback_register = callback.register +function callback.register(name, fn) + err("direct registering of callbacks is forbidden, use 'callback.add_to_callback'") +end +-- +-- Table with lists of functions for different callbacks. +local callback_functions = {} +-- Table that maps callback name to a list of descriptions of its added +-- functions. The order corresponds with `callback_functions`. +local callback_description = {} +-- +-- Table used to differentiate user callbacks from standard callbacks. Contains +-- user callbacks as keys. +local user_callbacks = {} +-- Table containing default functions for callbacks, which are called if either +-- a user created callback is defined, but doesn't have added functions or for +-- standard callbacks that are \"extended" (see `mlist_to_hlist` and its pre/post +-- filters below). +local default_functions = {} +-- +-- Table that maps standard (and later user) callback names to their types. +local callback_types = { + -- file discovery + find_read_file = "exclusive", + find_write_file = "exclusive", + find_font_file = "data", + find_output_file = "data", + find_format_file = "data", + find_vf_file = "data", + find_map_file = "data", + find_enc_file = "data", + find_pk_file = "data", + find_data_file = "data", + find_opentype_file = "data", + find_truetype_file = "data", + find_type1_file = "data", + find_image_file = "data", + + open_read_file = "exclusive", + read_font_file = "exclusive", + read_vf_file = "exclusive", + read_map_file = "exclusive", + read_enc_file = "exclusive", + read_pk_file = "exclusive", + read_data_file = "exclusive", + read_truetype_file = "exclusive", + read_type1_file = "exclusive", + read_opentype_file = "exclusive", + + -- data processing + process_input_buffer = "data", + process_output_buffer = "data", + process_jobname = "data", + + -- node list processing + contribute_filter = "simple", + buildpage_filter = "simple", + build_page_insert = "exclusive", + pre_linebreak_filter = "list", + linebreak_filter = "exclusive", + append_to_vlist_filter = "exclusive", + post_linebreak_filter = "reverselist", + hpack_filter = "list", + vpack_filter = "list", + hpack_quality = "list", + vpack_quality = "list", + process_rule = "exclusive", + pre_output_filter = "list", + hyphenate = "simple", + ligaturing = "simple", + kerning = "simple", + insert_local_par = "simple", + mlist_to_hlist = "exclusive", + + -- information reporting + pre_dump = "simple", + start_run = "simple", + stop_run = "simple", + start_page_number = "simple", + stop_page_number = "simple", + show_error_hook = "simple", + show_error_message = "simple", + show_lua_error_hook = "simple", + start_file = "simple", + stop_file = "simple", + call_edit = "simple", + finish_synctex = "simple", + wrapup_run = "simple", + + -- pdf related + finish_pdffile = "data", + finish_pdfpage = "data", + page_order_index = "data", + process_pdf_image_content = "data", + + -- font related + define_font = "exclusive", + glyph_not_found = "exclusive", + glyph_info = "exclusive", + + -- undocumented + glyph_stream_provider = "exclusive", +} +-- +-- Return a list containing descriptions of added callback functions for +-- specific callback. +function callback.callback_descriptions(name) + return callback_description[name] or {} +end + +local valid_callback_types = { + exclusive = true, + simple = true, + data = true, + list = true, + reverselist = true, +} +-- +-- Create a user callback that can only be called manually using +-- `call_callback`. A default function is only needed by "exclusive" callbacks. +function callback.create_callback(name, cbtype, default) + if callback_types[name] then + err("cannot create callback '"..name.."' - it already exists") + elseif not valid_callback_types[cbtype] then + err("cannot create callback '"..name.. "' with invalid callback type '"..cbtype.."'") + elseif ctype == "exclusive" and not default then + err("unable to create exclusive callback '"..name.."', default function is required") + end + + callback_types[name] = cbtype + default_functions[name] = default or nil + user_callbacks[name] = true +end +-- +-- Add a function to the list of functions executed when callback is called. +-- For standard luatex callback a proxy function that calls our machinery is +-- registered as the real callback function. This doesn't happen for user +-- callbacks, that are called manually by user using `call_callback` or for +-- standard callbacks that have default functions -- like `mlist_to_hlist` (see +-- below). +function callback.add_to_callback(name, fn, description) + if user_callbacks[name] or callback_functions[name] or default_functions[name] then + -- either: + -- a) user callback - no need to register anything + -- b) standard callback that has already been registered + -- c) standard callback with default function registered separately + -- (mlist_to_hlist) + elseif callback_types[name] then + -- This is a standard luatex callback with first function being added, + -- register a proxy function as a real callback. Assert, so we know + -- when things break, like when callbacks get redefined by future + -- luatex. + assert(callback_register(name, function(...) + return callback.call_callback(name, ...) + end)) + else + err("cannot add to callback '"..name.."' - no such callback exists") + end + + -- add function to callback list for this callback + callback_functions[name] = callback_functions[name] or {} + table.insert(callback_functions[name], fn) + + -- add description to description list + callback_description[name] = callback_description[name] or {} + table.insert(callback_description[name], description) +end +-- +-- Remove a function from the list of functions executed when callback is +-- called. If last function in the list is removed delete the list entirely. +function callback.remove_from_callback(name, description) + local descriptions = callback_description[name] + local index + for i, desc in ipairs(descriptions) do + if desc == description then + index = i + break + end + end + + table.remove(descriptions, index) + local fn = table.remove(callback_functions[name], index) + + if #descriptions == 0 then + -- Delete the list entirely to allow easy checking of "truthiness". + callback_functions[name] = nil + + if not user_callbacks[name] and not default_functions[name] then + -- this is a standard callback with no added functions and no + -- default function (i.e. not mlist_to_hlist), restore standard + -- behaviour by unregistering. + callback_register(name, nil) + end + end + + return fn, description +end +-- +-- helper iterator generator for iterating over reverselist callback functions +local function reverse_ipairs(t) + local i, n = #t + 1, 1 + return function() + i = i - 1 + if i >= n then + return i, t[i] + end + end +end +-- +-- Call all functions added to callback. This function handles standard +-- callbacks as well as user created callbacks. It can happen that this +-- function is called when no functions were added to callback -- like for user +-- created callbacks or `mlist_to_hlist` (see below), these are handled either +-- by a default function (like for `mlist_to_hlist` and those user created +-- callbacks that set a default function) or by doing nothing for empty +-- function list. +function callback.call_callback(name, ...) + local cbtype = callback_types[name] + -- either take added functions or the default function if there is one + local functions = callback_functions[name] or {default_functions[name]} + + if cbtype == nil then + err("cannot call callback '"..name.."' - no such callback exists") + elseif cbtype == "exclusive" then + -- only one function, atleast default function is guaranteed by + -- create_callback + return functions[1](...) + elseif cbtype == "simple" then + -- call all functions one after another, no passing of data + for _, fn in ipairs(functions) do + fn(...) + end + return + elseif cbtype == "data" then + -- pass data (first argument) from one function to other, while keeping + -- other arguments + local data = (...) + for _, fn in ipairs(functions) do + data = fn(data, select(2, ...)) + end + return data + end + + -- list and reverselist are like data, but "true" keeps data (head node) + -- unchanged and "false" ends the chain immediately + local iter + if cbtype == "list" then + iter = ipairs + elseif cbtype == "reverselist" then + iter = reverse_ipairs + end + + local head = (...) + local new_head + local changed = false + for _, fn in iter(functions) do + new_head = fn(head, select(2, ...)) + if new_head == false then + return false + elseif new_head ~= true then + head = new_head + changed = true + end + end + return not changed or head +end +-- +-- Create \"virtual" callbacks `pre/post_mlist_to_hlist_filter` by setting +-- `mlist_to_hlist` callback. The default behaviour of `mlist_to_hlist` is kept by +-- using a default function, but it can still be overriden by using +-- `add_to_callback`. +default_functions["mlist_to_hlist"] = node.mlist_to_hlist +callback.create_callback("pre_mlist_to_hlist_filter", "list") +callback.create_callback("post_mlist_to_hlist_filter", "reverselist") +callback_register("mlist_to_hlist", function(head, ...) + -- pre_mlist_to_hlist_filter + local new_head = callback.call_callback("pre_mlist_to_hlist_filter", head, ...) + if new_head == false then + node.flush_list(head) + return nil + elseif new_head ~= true then + head = new_head + end + + -- mlist_to_hlist means either added functions or standard luatex behavior + -- of node.mlist_to_hlist (handled by default function) + head = callback.call_callback("mlist_to_hlist", head, ...) + + -- post_mlist_to_hlist_filter + new_head = callback.call_callback("post_mlist_to_hlist_filter", head, ...) + if new_head == false then + node.flush_list(head) + return nil + elseif new_head ~= true then + head = new_head + end + return head +end) +-- +-- Compatibility with \LaTeX/ through luatexbase namespace. Needed for +-- luaotfload. +luatexbase = { + registernumber = registernumber, + attributes = attributes, + new_attribute = alloc.new_attribute, + callback_descriptions = callback.callback_descriptions, + create_callback = callback.create_callback, + add_to_callback = callback.add_to_callback, + remove_from_callback = callback.remove_from_callback, + call_callback = callback.call_callback, + callbacktypes = {} +} |