diff options
Diffstat (limited to 'luatexbase-mcb.dtx')
-rw-r--r-- | luatexbase-mcb.dtx | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/luatexbase-mcb.dtx b/luatexbase-mcb.dtx new file mode 100644 index 0000000..11c2a44 --- /dev/null +++ b/luatexbase-mcb.dtx @@ -0,0 +1,837 @@ +% \iffalse meta-comment +% +% Written in 2009, 2010 by Manuel Pégourié-Gonnard and Élie Roux. +% <mpg@elzevir.fr> +% <elie.roux@telecom-bretagne.eu> +% +% This work is under the CC0 license. +% +% This work consists of the main source file luatexbase-mcb.dtx +% and the derived files +% luatexbase-mcb.sty, mcb.lua, luatexbase-mcb.pdf, +% test-mcb-plain.tex test-mcb-latex.tex +% +% Unpacking: +% tex luatexbase-mcb.dtx +% Documentation: +% pdflatex luatexbase-mcb.dtx +% +%<*ignore> +\begingroup + \def\x{LaTeX2e}% +\expandafter\endgroup +\ifcase 0\ifx\install y1\fi\expandafter + \ifx\csname processbatchFile\endcsname\relax\else1\fi + \ifx\fmtname\x\else 1\fi\relax +\else\csname fi\endcsname +%</ignore> +%<*install> +\input docstrip.tex + +\keepsilent +\askforoverwritefalse + +\let\MetaPrefix\relax + +\preamble + +Copyright (C) 2009 by Elie Roux <elie.roux@telecom-bretagne.eu> + +This work is under the CC0 license. +See source file '\inFileName' for details. + +\endpreamble + +\let\MetaPrefix\DoubleperCent + +\generate{% + \usedir{tex/luatex/luatexbase}% + \file{luatexbase-mcb.sty}{\from{luatexbase-mcb.dtx}{texpackage}}% +} + +\generate{% + \usedir{doc/luatex/luatexbase}% + \file{test-mcb-plain.tex}{\from{luatexbase-mcb.dtx}{testplain}}% + \file{test-mcb-latex.tex}{\from{luatexbase-mcb.dtx}{testlatex}}% +} + +\def\MetaPrefix{-- } + +\def\luapostamble{% + \MetaPrefix^^J% + \MetaPrefix\space End of File `\outFileName'.% +} + +\def\currentpostamble{\luapostamble}% + +\generate{% + \usedir{tex/luatex/luatexbase}% + \file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}% +} + +\obeyspaces +\Msg{************************************************************************} +\Msg{*} +\Msg{* To finish the installation you have to move the following} +\Msg{* files into a directory searched by TeX:} +\Msg{*} +\Msg{* luatexbase-mcb.sty mcb.lua} +\Msg{*} +\Msg{* Happy TeXing!} +\Msg{*} +\Msg{************************************************************************} + +\endbatchfile +%</install> +%<*ignore> +\fi +%</ignore> +%<*driver> +\documentclass{ltxdoc} +\input{lltxb-dtxstyle} +\begin{document} + \DocInput{luatexbase-mcb.dtx}% +\end{document} +%</driver> +% \fi +% +% \CheckSum{0} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% \title{The \textsf{luatexbase-mcb} package} +% \date{2009/09/18 v0.93} +% \author{% +% Manuel P\'egouri\'e-Gonnard \\ \email{mpg@elzevir.fr} \and +% \'Elie Roux \\ \email{elie.roux@telecom-bretagne.eu}} +% +% \maketitle +% +% \begin{abstract} +% This package manages the callback adding and removing, by adding +% \texttt{callback.add} and \texttt{callback.remove}, and overwriting +% \texttt{callback.register}. It also allows to create and call new callbacks. +% For an introduction on this package (among others), please refer to the +% document \texttt{luatextra-reference.pdf}. +% \par\textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils} +% has been previously loaded. (This is a temporary limitation.) +% \end{abstract} +% +% \tableofcontents +% +% \section{Documentation} +% +% Lua\TeX\ provides an extremely interesting feature, named callbacks. It +% allows to call some lua functions at some points of the \TeX\ algorithm (a +% \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc. +% The Lua\TeX\ core offers a function called \texttt{callback.register} that +% enables to register a function in a callback. +% +% The problem with \texttt{callback.register} is that is registers only one +% function in a callback. For a lot of callbacks it can be common to have +% several packages registering their function in a callback, and thus it is +% impossible with them to be compatible with each other. +% +% This package solves this problem by adding mainly one new function +% \texttt{callback.\\add} that adds a function in a callback. With this +% function it is possible for packages to register their function in a +% callback without overwriting the functions of the other packages. +% +% The functions are called in a certain order, and when a package registers a +% callback it can assign a priority to its function. Conflicts can still +% remain even with the priority mechanism, for example in the case where two +% packages want to have the highest priority. In these cases the packages have +% to solve the conflicts themselves. +% +% This package also privides a way to create and call new callbacks, in +% addition to the default Lua\TeX\ callbacks. +% +% \subsubsection*{Limitations} +% +% This package only works for callbacks where it's safe to add multiple +% functions without changing the functions' signatures. There are callbacks, +% though, where registering several functions is not possible without changing +% the function's signatures, like for example the readers callbacks. These +% callbacks take a filename and give the datas in it. One solution would be to +% change the functions' signature to open it when the function is the first, +% and to take the datas and modify them eventually if they are called after +% the first. But it seems rather fragile and useless, so it's not implemented. +% With these callbacks, in this package we simply execute the first function +% in the list. +% +% Other callbacks in this case are \texttt{define\_font} and +% \texttt{open\_read\_file}. There is though a solution for several packages +% to use these callbacks, see the implementation of \texttt{luatextra}. +% +% \section{Implementation} +% +% \subsection{\tex package} +% +% \begin{macrocode} +%<*texpackage> +% \end{macrocode} +% +% \subsubsection{Preliminaries} +% +% Reload protection, especially for \plaintex. +% +% \begin{macrocode} + \csname lltxb@mcb@loaded\endcsname +\expandafter\let\csname lltxb@mcb@loaded\endcsname\endinput +% \end{macrocode} +% +% Catcode defenses. +% +% \begin{macrocode} +\begingroup + \catcode123 1 % { + \catcode125 2 % } + \catcode 35 6 % # + \toks0{}% + \def\x{}% + \def\y#1 #2 {% + \toks0\expandafter{\the\toks0 \catcode#1 \the\catcode#1}% + \edef\x{\x \catcode#1 #2}}% + \y 123 1 % { + \y 125 2 % } + \y 35 6 % # + \y 10 12 % ^^J + \y 34 12 % " + \y 36 3 % $ $ + \y 39 12 % ' + \y 40 12 % ( + \y 41 12 % ) + \y 42 12 % * + \y 43 12 % + + \y 44 12 % , + \y 45 12 % - + \y 46 12 % . + \y 47 12 % / + \y 60 12 % < + \y 61 12 % = + \y 64 11 % @ (letter) + \y 62 12 % > + \y 95 12 % _ (other) + \y 96 12 % ` + \edef\y#1{\endgroup\edef#1{\the\toks0\relax}\x}% +\expandafter\y\csname lltxb@mcb@AtEnd\endcsname +% \end{macrocode} +% +% Package declaration. +% +% \begin{macrocode} +\begingroup + \expandafter\ifx\csname ProvidesPackage\endcsname\relax + \def\x#1[#2]{\immediate\write16{Package: #1 #2}} + \else + \let\x\ProvidesPackage + \fi +\expandafter\endgroup +\x{luatexbase-mcb}[2010/09/11 v0.93 Callback management for LuaTeX] +% \end{macrocode} +% +% Make sure \luatex is used. +% +% \begin{macrocode} +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname RequirePackage\endcsname\relax + \input ifluatex.sty +\else + \RequirePackage{ifluatex} +\fi +\ifluatex\else + \begingroup + \expandafter\ifx\csname PackageWarningNoLine\endcsname\relax + \def\x#1#2{\begingroup\newlinechar10 + \immediate\write16{Package #1 warning: #2}\endgroup} + \else + \let\x\PackageWarningNoLine + \fi + \expandafter\endgroup + \x{luatexbase-mcb}{LuaTeX is required for this package. Aborting.} + \lltxb@mcb@AtEnd + \expandafter\endinput +\fi +% \end{macrocode} +% +% \subsubsection{Load supporting Lua module} +% +% First load \pk{luatexbase-loader} (hence \pk{luatexbase-compat}), then +% the supporting Lua module. +% +% \begin{macrocode} +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname RequirePackage\endcsname\relax + \input luatexbase-modutils.sty +\else + \RequirePackage{luatexbase-modutils} +\fi +\luatexbase@directlua{require('luatexbase.mcb')} +% \end{macrocode} +% +% That's all folks! +% +% \begin{macrocode} +\lltxb@mcb@AtEnd +%</texpackage> +% \end{macrocode} +% +% \subsection{Lua module} +% +% \begin{macrocode} +%<*lua> +% \end{macrocode} +% +% \subsubsection{Module identification} +% +% \begin{macrocode} +module('luatexbase', package.seeall) +luatexbase.provides_module({ + name = "luamcallbacks", + version = 0.93, + date = "2009/09/18", + description = "register several functions in a callback", + author = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard", + copyright = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard", + license = "CC0", +}) +% \end{macrocode} +% +% Shortcuts for error functions. +% +% \begin{macrocode} +local log = log or function(...) + luatexbase.module_log('luamcallbacks', string.format(...)) +end +local info = info or function(...) + luatexbase.module_info('luamcallbacks', string.format(...)) +end +local warning = warning or function(...) + luatexbase.module_warning('luamcallbacks', string.format(...)) +end +local err = err or function(...) + luatexbase.module_error('luamcallbacks', string.format(...)) +end +% \end{macrocode} +% +% \subsubsection{Initialisations} +% +% \texttt{callbacklist} is the main list, that contains the callbacks as +% keys and a table of the registered functions a values. +% +% \begin{macrocode} +local callbacklist = callbacklist or { } +% \end{macrocode} +% +% A table with the default functions of the created callbacks. See +% \texttt{create} for further informations. +% +% \begin{macrocode} +local lua_callbacks_defaults = { } +% \end{macrocode} +% +% There are 4 types of callback: +% \begin{itemize} +% \item the ones taking a list of nodes and returning a boolean and +% eventually a new head (\texttt{list}) +% \item the ones taking datas and returning the modified ones +% (\texttt{data}) +% \item the ones that can't have multiple functions registered in them +% (\texttt{first}) +% \item the ones for functions that don't return anything (\texttt{simple}) +% \end{itemize} +% +% \begin{macrocode} +local list = 1 +local data = 2 +local first = 3 +local simple = 4 +% \end{macrocode} +% +% \texttt{callbacktypes} is the list that contains the callbacks as keys +% and the type (list or data) as values. +% +% \begin{macrocode} +local callbacktypes = callbacktypes or { + buildpage_filter = simple, + token_filter = first, + pre_output_filter = list, + hpack_filter = list, + process_input_buffer = data, + mlist_to_hlist = list, + vpack_filter = list, + define_font = first, + open_read_file = first, + linebreak_filter = list, + post_linebreak_filter = list, + pre_linebreak_filter = list, + start_page_number = simple, + stop_page_number = simple, + start_run = simple, + show_error_hook = simple, + stop_run = simple, + hyphenate = simple, + ligaturing = simple, + kerning = data, + find_write_file = first, + find_read_file = first, + find_vf_file = data, + find_map_file = data, + find_format_file = data, + find_opentype_file = data, + find_output_file = data, + find_truetype_file = data, + find_type1_file = data, + find_data_file = data, + find_pk_file = data, + find_font_file = data, + find_image_file = data, + find_ocp_file = data, + find_sfd_file = data, + find_enc_file = data, + read_sfd_file = first, + read_map_file = first, + read_pk_file = first, + read_enc_file = first, + read_vf_file = first, + read_ocp_file = first, + read_opentype_file = first, + read_truetype_file = first, + read_font_file = first, + read_type1_file = first, + read_data_file = first, +} +% \end{macrocode} +% +% In Lua\TeX\ version 0.43, a new callback called |process_output_buffer| +% appeared, so we enable it. Test the version using the compat package for, +% well, compatibility. +% +% \begin{macrocode} +if luatexbase.luatexversion > 42 then + callbacktypes["process_output_buffer"] = data +end +% \end{macrocode} +% +% As we overwrite \texttt{callback.register}, we save it as +% \texttt{internalregister}. +% +% \begin{macrocode} +local internalregister = internalregister or callback.register +% \end{macrocode} +% +% \subsubsection{Unsorted stuff} +% +% A simple function we'll use later to understand the arguments of the +% \texttt{create} function. It takes a string and returns the type +% corresponding to the string or nil. +% +% \begin{macrocode} +local function str_to_type(str) + if str == 'list' then + return list + elseif str == 'data' then + return data + elseif str == 'first' then + return first + elseif str == 'simple' then + return simple + else + return nil + end +end +% \end{macrocode} +% +% This function and the following ones are only internal. This one is the +% handler for the first type of callbacks: the ones that take a list head +% and return true, false, or a new list head. +% +% \begin{macrocode} +-- local +function listhandler (name) + return function(head,...) + local l = callbacklist[name] + if l then + local done = true + for _, f in ipairs(l) do + -- the returned value is either true or a new head plus true + rtv1, rtv2 = f.func(head,...) + if type(rtv1) == 'boolean' then + done = rtv1 + elseif type (rtv1) == 'userdata' then + head = rtv1 + end + if type(rtv2) == 'boolean' then + done = rtv2 + elseif type(rtv2) == 'userdata' then + head = rtv2 + end + if done == false then + err("function \"%s\" returned false in callback '%s'", + f.description, name) + end + end + return head, done + else + return head, false + end + end +end +% \end{macrocode} +% +% The handler for callbacks taking datas and returning modified ones. +% +% \begin{macrocode} +local function datahandler (name) + return function(data,...) + local l = callbacklist[name] + if l then + for _, f in ipairs(l) do + data = f.func(data,...) + end + end + return data + end +end +% \end{macrocode} +% +% This function is for the handlers that don't support more than one +% functions in them. In this case we only call the first function of the +% list. +% +% \begin{macrocode} +local function firsthandler (name) + return function(...) + local l = callbacklist[name] + if l then + local f = l[1].func + return f(...) + else + return nil, false + end + end +end +% \end{macrocode} +% +% Handler for simple functions that don't return anything. +% +% \begin{macrocode} +local function simplehandler (name) + return function(...) + local l = callbacklist[name] + if l then + for _, f in ipairs(l) do + f.func(...) + end + end + end +end +% \end{macrocode} +% +% \subsubsection{Public functions} +% +% The main function. The signature is \texttt{add (name, +% func, description, priority)} with \texttt{name} being the name of the +% callback in which the function is added; \texttt{func} is the added +% function; \texttt{description} is a small character string describing the +% function, and \texttt{priority} an optional argument describing the +% priority the function will have. +% +% The functions for a callbacks are added in a list (in +% \texttt{callbacklist\\.callbackname}). If they have no +% priority or a high priority number, they will be added at the end of the +% list, and will be called after the others. If they have a low priority +% number, the will be added at the beginning of the list and will be called +% before the others. +% +% Something that must be made clear, is that there is absolutely no +% solution for packages conflicts: if two packages want the top priority on +% a certain callback, they will have to decide the priority they will give +% to their function themselves. Most of the time, the priority is not needed. +% +% \begin{macrocode} +function add_to_callback (name,func,description,priority) + if type(func) ~= "function" then + err("unable to add function, no proper function passed") + return + end + if not name or name == "" then + err("unable to add function, no proper callback name passed") + return + elseif not callbacktypes[name] then + err("unable to add function, '%s' is not a valid callback", name) + return + end + if not description or description == "" then + err("unable to add function to '%s', no proper description passed", + name) + return + end + if priority_in_callback(name, description) ~= 0 then + warning("function '%s' already registered in callback '%s'", + description, name) + end + local l = callbacklist[name] + if not l then + l = {} + callbacklist[name] = l + if not lua_callbacks_defaults[name] then + if callbacktypes[name] == list then + internalregister(name, listhandler(name)) + elseif callbacktypes[name] == data then + internalregister(name, datahandler(name)) + elseif callbacktypes[name] == simple then + internalregister(name, simplehandler(name)) + elseif callbacktypes[name] == first then + internalregister(name, firsthandler(name)) + else + err("unknown callback type") + end + end + end + local f = { + func = func, + description = description, + } + priority = tonumber(priority) + if not priority or priority > #l then + priority = #l+1 + elseif priority < 1 then + priority = 1 + end + if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then + warning("several callbacks registered in callback '%s', " + .."only the first function will be active.", name) + end + table.insert(l,priority,f) + log("inserting function '%s' at position %s in callback list for '%s'", + description, priority, name) +end +% \end{macrocode} +% +% The function that removes a function from a callback. The signature is +% \texttt{remove (name, description)} with \texttt{name} being +% the name of callbacks, and description the description passed to +% \texttt{add}. +% +% \begin{macrocode} +function remove_from_callback (name, description) + if not name or name == "" then + err("unable to remove function, no proper callback name passed") + return + elseif not callbacktypes[name] then + err("unable to remove function, '%s' is not a valid callback", name) + return + end + if not description or description == "" then + err( + "unable to remove function from '%s', no proper description passed", + name) + return + end + local l = callbacklist[name] + if not l then + err("no callback list for '%s'",name) + return + end + for k,v in ipairs(l) do + if v.description == description then + table.remove(l,k) + log("removing function '%s' from '%s'",description,name) + if not next(l) then + callbacklist[name] = nil + if not lua_callbacks_defaults[name] then + internalregister(name, nil) + end + end + return + end + end + warning("unable to remove function '%s' from '%s'",description,name) +end +% \end{macrocode} +% +% This function removes all the functions registered in a callback. +% +% \begin{macrocode} +function reset_callback (name) + if not name or name == "" then + err("unable to reset, no proper callback name passed") + return + elseif not callbacktypes[name] then + err("reset error, '%s' is not a valid callback", name) + return + end + if not lua_callbacks_defaults[name] then + internalregister(name, nil) + end + local l = callbacklist[name] + if l then + log("resetting callback list '%s'",name) + callbacklist[name] = nil + end +end +% \end{macrocode} +% +% This first function creates a new callback. The signature is +% \texttt{create(name, ctype, default)} where \texttt{name} is the name of +% the new callback to create, \texttt{ctype} is the type of callback, and +% \texttt{default} is the default function to call if no function is +% registered in this callback. +% +% The created callback will behave the same way Lua\TeX\ callbacks do, you +% can add and remove functions in it. The difference is that the callback +% is not automatically called, the package developer creating a new +% callback must also call it, see next function. +% +% \begin{macrocode} +function create_callback(name, ctype, default) + if not name then + err("unable to call callback, no proper name passed", name) + return nil + end + if not ctype or not default then + err("unable to create callback '%s': " + .."callbacktype or default function not specified", name) + return nil + end + if callbacktypes[name] then + err("unable to create callback '%s', callback already exists", name) + return nil + end + local temp = str_to_type(ctype) + if not temp then + err("unable to create callback '%s', type '%s' undefined", name, ctype) + return nil + end + ctype = temp + lua_callbacks_defaults[name] = default + callbacktypes[name] = ctype +end +% \end{macrocode} +% +% This function calls a callback. It can only call a callback created by +% the \texttt{create} function. +% +% \begin{macrocode} +function call_callback(name, ...) + if not name then + err("unable to call callback, no proper name passed", name) + return nil + end + if not lua_callbacks_defaults[name] then + err("unable to call lua callback '%s', unknown callback", name) + return nil + end + local l = callbacklist[name] + local f + if not l then + f = lua_callbacks_defaults[name] + else + if callbacktypes[name] == list then + f = listhandler(name) + elseif callbacktypes[name] == data then + f = datahandler(name) + elseif callbacktypes[name] == simple then + f = simplehandler(name) + elseif callbacktypes[name] == first then + f = firsthandler(name) + else + err("unknown callback type") + end + end + return f(...) +end +% \end{macrocode} +% +% This function tells if a function has already been registered in a +% callback, and gives its current priority. The arguments are the name of +% the callback and the description of the function. If it has already been +% registered, it gives its priority, and if not it returns false. +% +% \begin{macrocode} +function priority_in_callback (name, description) + if not name or name == "" + or not callbacktypes[name] + or not description then + return 0 + end + local l = callbacklist[name] + if not l then return 0 end + for p, f in pairs(l) do + if f.description == description then + return p + end + end + return 0 +end +% \end{macrocode} +% +% Finally we +% overwrite \texttt{callback.register} so that it outputs an error. +% +% \begin{macrocode} +callback.register = function () +err("function callback.register has been deleted by luamcallbacks, " +.."please use callback.add instead.") +end +% \end{macrocode} +% +% That's all folks! +% +% \begin{macrocode} +%</lua> +% \end{macrocode} +% +% \section{Test files} +% +% A few basic tests for Plain and LaTeX. +% +% \begin{macrocode} +%<testplain>\input luatexbase-mcb.sty +%<testlatex>\RequirePackage{luatexbase-mcb} +%<*testplain,testlatex> +\catcode 64 11 +\luatexbase@directlua{ + local function one(head,...) + texio.write_nl("I'm number 1") + return head, true + end + + local function two(head,...) + texio.write_nl("I'm number 2") + return head, true + end + + local function three(head,...) + texio.write_nl("I'm number 3") + return head, true + end + + luatexbase.add_to_callback("hpack_filter",one,"my sample function one",1) + luatexbase.add_to_callback("hpack_filter",two,"my sample function two",2) + luatexbase.add_to_callback("hpack_filter",three,"my sample function three",1) + + luatexbase.remove_from_callback("hpack_filter","my sample function three") +} +%</testplain,testlatex> +%<testplain>\bye +%<testlatex>\stop +% \end{macrocode} +% +% \Finale +\endinput |