1

I read the post Automatically alter letterspacing (intra-word spacing) to avoid loose lines with LuaLaTeX which I would like to adapt to create a command \InterletterGlueEnable with two arguments. The problem is that I can't pass the values of these two arguments in the command written in Lua. Would you know how to do this?
Here is my MWE:

\documentclass{article}
\usepackage{luacode, xargs}
\usepackage[a4paper, landscape, left=1cm, right=1cm, top=1cm, bottom=1cm, nohead]{geometry}

\def\sample{%
Even though using "lorem ipsum" often arouses curiosity due to its resemblance to classical Latin, it is not intended to have meaning. Where text is visible in a document, people tend to focus on the textual content rather than upon overall presentation, so publishers use lorem ipsum when displaying a typeface or design in order to direct the focus to presentation. "Lorem ipsum" also approximates a typical distribution of spaces in English.\par
The most common lorem ipsum text reads as follows: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. The text is derived from Cicero's De Finibus Bonorum et Malorum (On the Ends of Goods and Evils, or alternatively [About] The Purposes of Good and Evil ).\par
}

\begin{luacode}
local interletter_glue = node.new('glue')
interletter_glue.width = tex.sp(0)
-- interletter_glue.stretch = tex.sp('5pt')
-- interletter_glue.shrink = tex.sp('1pt')
local interletter_pen = node.new('penalty')
interletter_pen.penalty = 10000

function add_interletter_glue(head, interletter_glue.stretch, interletter_glue.shrink) 
    for glyph in node.traverse_id(node.id('glyph'), head) do
        if glyph.prev and (glyph.prev.id == node.id('glyph') 
            or glyph.prev.id == node.id('disc') 
            or glyph.prev.id == node.id('kern')) then
            local g = node.copy(interletter_glue)
            node.insert_before(head, glyph, g)
            node.insert_before(head, g, node.copy(interletter_pen))
        end
    end
    return head
end
\end{luacode}

\newcommandx{\InterletterGlueEnable}[2][1=5pt, 2=1pt, usedefault]{\directlua{luatexbase.add_to_callback("pre_linebreak_filter", add_interletter_glue(,tex.sp('#1'),tex.sp('#2')), "variableInterletterSpacing")}}
\newcommand{\InterletterGlueDisable}{\directlua{luatexbase.remove_from_callback("pre_linebreak_filter", "variableInterletterSpacing")}}

\begin{document}
\begin{minipage}[t]{13em}
    \noindent\textbf{No tracking :}

    \sample
\end{minipage}
\qquad
\begin{minipage}[t]{13em}
    \InterletterGlueEnable
    \noindent\textbf{Tracking 1:}
    
    \sample
    \InterletterGlueDisable{}
\end{minipage}
\qquad
\begin{minipage}[t]{13em}
    \InterletterGlueEnable[0.5pt][0.1pt]
    \noindent \textbf{Tracking 2:}
    
    \sample
    \InterletterGlueDisable{}
\end{minipage}
\end{document}
3
  • Off-topic: the options left=1cm, right=1cm, top=1cm, bottom=1cm may be replaced with the single option margin=1cm.
    – Mico
    Jun 9, 2021 at 10:38
  • @MarcelKrüger I don't think that is where the problem lies. With the xargs package I can define a command with several optional arguments and their default values. What I can't do is to find the right syntax to pass these arguments so that they are taken into account by the Lua command.
    – B Legrand
    Jun 9, 2021 at 11:53
  • @BLegrand Sorry, I missed the x. Jun 9, 2021 at 12:03

1 Answer 1

2

There are multiple issues:

  1. The parameter names of a Lua function must be normal variable names which declare variables with function scope. They can not be fields of existing variables. Therefore you can't use
    function add_interletter_glue(head, interletter_glue.stretch, interletter_glue.shrink)
    
    but have to use something like
    function add_interletter_glue(head, ilg_stretch, ilg_shrink)
      interletter_glue.stretch = ilg_stretch
      interletter_glue.shrink = ilg_shrink
    
    instead.
  2. The argument list of a Lua function can not start with a comma, so
    add_interletter_glue(,tex.sp('#1'),tex.sp('#2'))
    
    is not allowed. To fix some parameters of a function while still retaining a function with arguments, you can use Closures and functions returning functions. E.g. add_interletter_glue can be written as
    function add_interletter_glue(ilg_stretch, ilg_shrink)
        return function(head) 
            interletter_glue.stretch, interletter_glue.shrink = ilg_stretch, ilg_shrink
            for glyph in node.traverse_id(node.id('glyph'), head) do
                ...
            end
            return head
        end
    end
    
    to become a function with two arguments returning a function with one argument which can be used as a callback.

This can be combined to

\documentclass{article}
\usepackage{luacode}
\usepackage[a4paper, landscape, left=1cm, right=1cm, top=1cm, bottom=1cm, nohead]{geometry}

\def\sample{%
Even though using "lorem ipsum" often arouses curiosity due to its resemblance to classical Latin, it is not intended to have meaning. Where text is visible in a document, people tend to focus on the textual content rather than upon overall presentation, so publishers use lorem ipsum when displaying a typeface or design in order to direct the focus to presentation. "Lorem ipsum" also approximates a typical distribution of spaces in English.\par
The most common lorem ipsum text reads as follows: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. The text is derived from Cicero's De Finibus Bonorum et Malorum (On the Ends of Goods and Evils, or alternatively [About] The Purposes of Good and Evil ).\par
}

\begin{luacode}
local interletter_glue = node.new('glue')
interletter_glue.width = 0
local interletter_pen = node.new('penalty')
interletter_pen.penalty = 10000

function add_interletter_glue(interletter_glue_stretch, interletter_glue_shrink) 
  return function(head)
    for glyph in node.traverse_id(node.id('glyph'), head) do
        if glyph.prev and (glyph.prev.id == node.id('glyph') 
          or glyph.prev.id == node.id('disc') 
          or glyph.prev.id == node.id('kern')) then
        local g = node.copy(interletter_glue)
        g.attr = glyph.attr
        g.stretch = interletter_glue_stretch
        g.shrink = interletter_glue_shrink
        node.insert_before(head, glyph, g)
        local p = node.copy(interletter_pen)
        p.attr = glyph.attr
        node.insert_before(head, g, p)
      end
    end
    return head
  end
end
\end{luacode}

\NewDocumentCommand \InterletterGlueEnable {O{5pt} O{1pt}}{\directlua{luatexbase.add_to_callback("pre_linebreak_filter", add_interletter_glue(tex.sp('#1'),tex.sp('#2')), "variableInterletterSpacing")}}
\NewDocumentCommand \InterletterGlueDisable {}{\directlua{luatexbase.remove_from_callback("pre_linebreak_filter", "variableInterletterSpacing")}}

\begin{document}
\begin{minipage}[t]{13em}
  \noindent\textbf{No tracking :}

  \sample
\end{minipage}
\qquad
\begin{minipage}[t]{13em}
  \InterletterGlueEnable
  \noindent\textbf{Tracking 1:}
  
  \sample
  \InterletterGlueDisable
\end{minipage}
\qquad
\begin{minipage}[t]{13em}
  \InterletterGlueEnable[0.5pt][0.1pt]
  \noindent \textbf{Tracking 2:}
  
  \sample
  \InterletterGlueDisable
\end{minipage}
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .