Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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

share|improve this 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
add comment

2 Answers

up vote 21 down vote accepted
+300

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

share|improve this answer
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 '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
show 1 more comment

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,
(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
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).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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