[Tex/LaTex] Porting the luatex/ConTeXt module “translate” to lualatex

contextligaturesluatex

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] 
\translateinput[cufflink][cuff|*|link] 

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

\starttext

Ligatures not disabled:\\
shelfful selfish halflife cufflink 

\medskip
\enableinputtranslation 
Ligatures disabled:\\
shelfful selfish halflife cufflink
\stoptext

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

Best Answer

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()
    luatexbase.add_to_callback('process_input_buffer', filter, 'filter')
  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} 
\translateinput{cufflink}{cuff|*|link}

\begin{document}

Ligatures not disabled:\\
shelfful selfish halflife cufflink 

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

\medskip
\disableinputtranslation
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
\end{document}

Run with lualatex test.

The output: filteroutput