# Porting the luatex/ConTeXt module “translate” to lualatex

In this question, which I posted a few weeks ago to TeX Stack Exchange, I asked how one should go about disabling specific ligatures (such as ff, fi, fl, and ffl) automatically for a list of pre-defined words. The answer I received was based on ConTeXt, which can access lua(tex) commands directly. In particular, the code that @Aditya came up with includes the commands

\usemodule[translate]
\translateinput[halflife][half|*|life]


The ConTeXt command \usemodule is similar to the LaTeX command \usepackage. The \translateinput command provided by the translate module (which is contained in the file m-translate.mkiv) relies on the lua function resolvers.openers.helpers.textlineactions, which hooks into the function that luatex uses to read files. According to @Aditya,

no one has written similar functionality for lualatex. Perhaps, you could ask it as a separate question, and one of the lualatex experts will be able to answer with a solution.

Here, then, is my question: Can someone provide the lualatex equivalent of the following ConTeXt code (which may involve re-writing/adapting the translate module to lualatex):

\usemodule[translate]

\translateinput[shelfful][shelf|*|ful]
\translateinput[selfish][self|*|ish]
\translateinput[halflife][half|*|life]

\definetextmodediscretionary * {\prewordbreak\discretionary{-}{}%
{\kern0.0pt}\prewordbreak}

\starttext

Ligatures not disabled:\\

\medskip
\enableinputtranslation
Ligatures disabled:\\
\stoptext


(Note that while a 0-kern separator seems to be appropriate for the words shelfful and selfish typeset with CM, it's not quite adequate for the f-l and ff-l ligatures. However, I'll leave the question of how to define commands that allow for distinct kerning amounts for various combinations of ligated letters for a different, future question...)

-
Another advantage of such a module (err...I mean package) will be true block comments in LaTeX –  Aditya Nov 22 '11 at 4:58
I doubt that is easily possible. It may turn out that this module is too much dependent on ConTeXt itself. The wise thing is that you, yourself devise something independent of ConTeXt. –  IRAN Nov 24 '11 at 4:27
@VafaKhalighi: Thanks for this suggestion. Unfortunately, because I'm not (yet) sufficiently familiar with lua(la)tex and ConTeXt, I can't take you up on it. That's also why I've posted this question in the first place. :-) –  Mico Nov 24 '11 at 16:00

This is more a proof-of-concept than a real bulletproof style file, but it does what you request:

The style file (filterltx.sty)

\ProvidesPackage{filterltx}
\RequirePackage{luatexbase,luacode}

\begin{luacode*}
do
local replace = {}

local filter = function ( buf )
local start,stop,init,pos
local positions = {}
for k,v in pairs(replace) do
local init = 1
repeat
start,stop = string.find(string.lower(buf),k,init,true)
if start then
init = stop
pos = string.find(v,"|*|",1,plain)
positions[#positions + 1] = pos + start - 2
end
until start == nil
end
table.sort(positions)
for i = #positions,1,-1 do
buf = string.sub(buf,1,positions[i] ) .. [[\penalty10000\discretionary{-}{}{\kern.03em}\nobreak \hskip 0pt plus0pt minus0pt]] .. string.sub(buf, positions[i] + 1)
end
return buf
end

function enablefilter()
end

function disablefilter()
luatexbase.remove_from_callback('process_input_buffer', 'filter')
end

function translateinput( arg1,arg2 )
replace[arg1] = arg2
end

end
\end{luacode*}

\newcommand\enableinputtranslation{
\directlua{enablefilter()}
}
\newcommand\disableinputtranslation{
\directlua{disablefilter()}
}
\newcommand\translateinput[2]{
\directlua{translateinput("\luatexluaescapestring{#1}","\luatexluaescapestring{#2}")}
}


and the test document (test.tex):

\documentclass{article}
\usepackage{filterltx}

\translateinput{shelfful}{shelf|*|ful}
\translateinput{selfish}{self|*|ish}
\translateinput{halflife}{half|*|life}

\begin{document}

Ligatures not disabled:\\

\medskip
\enableinputtranslation
Ligatures disabled:\\

\medskip
\disableinputtranslation
Ligatures not disabled:\\

% to make sure the words still hyphenate:
% yields: shelf- ful self- ish half- life cuff- link
\end{document}


Run with lualatex test.

The output:

-
Thanks for the great code. Unfortunately, it appears to have a rather severe negative side-effect. :-( Specifically, TeX no longer wants to hyphenate the words at the junction points (shelf-ful, self-ish, half-life cuff-link) even though these junction points are valid hyphenation points. May I ask you, somewhat belatedly, to investigate how this problem can be solved? –  Mico Dec 1 '11 at 3:36
@Mico I have edited my answer to fix the broken hyphenation. –  topskip Dec 1 '11 at 9:52
Perfect now. Many thanks! –  Mico Dec 1 '11 at 12:10
One more question, if you don't mind: Is there a version of lua's string.gsub function that's not case-sensitive? More specifically, it should preserve the (upper/lower) caes of letters automatically. An ideal solution would let the command \translateinput{auflage}{auf|*|lage} operate on the word Auflage as well -- and generate Auf|*|lage. Thanks in advance! –  Mico Dec 13 '11 at 22:39
@Mico Done. I have changed the algorithm a bit... Drawback: currently only one ligature per word can be suppressed. The rest is left as an exercise to the reader. –  topskip Dec 14 '11 at 10:30

The basic functionality is part of luatex. For example, the following code (in luatex plain format) changes "hello" to "hi".

\directlua{
local gsub = string.gsub
local function translate(line)
return gsub(line, "hello", "hi")
end

callback.register("process_input_buffer", translate)
}

hello world

why say hello
\bye


The ConTeXt code just provides syntax sugar so that you can easily define multiple translations. I tried to translate this code to lualatex but in lualatex I get an error

uatexbase-mcb error: function callback.register has been trapped,

stack traceback:
[C]: in function 'error'
.../share/texmf-dist/tex/luatex/luatexbase/modutils.lua:26: in function 'modul
e_error_int'
.../share/texmf-dist/tex/luatex/luatexbase/modutils.lua:45: in function 'err'
/usr/share/texmf-dist/tex/luatex/luatexbase/mcb.lua:85: in function 'register'

<\directlua >:7: in main chunk.
\luacode@execute ...oup \luatexbase@directlua {#1}

l.11 \end{luacode}


but I could not get the code to work with luatexbase.add_to_callback either.

The above plain TeX code shows that basic functionality is part of luatex and should be easily accessible in lualatex as well (once you figure out the appropriate renaming done by luacode package).

-