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


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):



\definetextmodediscretionary * {\prewordbreak\discretionary{-}{}%


Ligatures not disabled:\\
shelfful selfish halflife cufflink 

Ligatures disabled:\\
shelfful selfish halflife cufflink

enter image description here

(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, 2011 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, 2011 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, 2011 at 16:00

2 Answers 2


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

The style file (filterltx.sty)


  local replace = {}

  local filter = function ( buf )
    local start,stop,init,pos
    local positions = {}
    for k,v in pairs(replace) do
      local init = 1
        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
      until start == nil
      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)
    return buf

  function enablefilter()
    luatexbase.add_to_callback('process_input_buffer', filter, 'filter')

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

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



and the test document (test.tex):




Ligatures not disabled:\\
shelfful selfish halflife cufflink 

Ligatures disabled:\\
shelfful selfish halflife cufflink\\
Shelfful Selfish Halflife Cufflink

Ligatures not disabled:\\
shelfful selfish halflife cufflink 

% to make sure the words still hyphenate:
% \showhyphens{shelfful selfish halflife cufflink}
% yields: shelf- ful self- ish half- life cuff- link

Run with lualatex test.

The output: filteroutput

  • 1
    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, 2011 at 3:36
  • @Mico I have edited my answer to fix the broken hyphenation.
    – topskip
    Dec 1, 2011 at 9:52
  • 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, 2011 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, 2011 at 10:30

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

  local gsub = string.gsub
  local function translate(line)
      return gsub(line, "hello", "hi")

  callback.register("process_input_buffer", translate)

hello world

why say hello

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,
(luatexbase-mcb)             please use luatexbase.add_to_callback instead.

stack traceback:
    [C]: in function 'error'
    .../share/texmf-dist/tex/luatex/luatexbase/modutils.lua:26: in function 'modul
    .../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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.