summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/optex/base/optex.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/optex/base/optex.lua')
-rw-r--r--Master/texmf-dist/tex/optex/base/optex.lua356
1 files changed, 356 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/optex/base/optex.lua b/Master/texmf-dist/tex/optex/base/optex.lua
new file mode 100644
index 00000000000..2d83f38ed6f
--- /dev/null
+++ b/Master/texmf-dist/tex/optex/base/optex.lua
@@ -0,0 +1,356 @@
+-- This is part of the 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 = {}
+function alloc.new_attribute(name)
+ local cnt = tex.count["_attributealloc"] + 1
+ if cnt > 65534 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
+--
+-- `provides_module` is needed by older version of luaotfload
+provides_module = function() 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",
+ input_level_string = "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",
+ provide_charproc_data = "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,
+ provides_module = provides_module,
+ 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 = {}
+}