0

The letterspace feature of luaotfload does not work in math mode. I tried to implement my own feature as follows:

\documentclass{minimal}

\directlua{
do
  local function mathletterspace(tfmdata, value)
    local ls = tonumber (value)
    if not ls then
      luaotfload.log.report ("both", 0, "letterspace",
                 "Invalid argument to letterspace.")
      return
    end
    if tfmdata.parameters.factor then
      ls = ls * tfmdata.parameters.factor
    end

    for idx,chr in next, tfmdata.characters do
      if chr and chr.width then
        chr.width = chr.width + 2*ls
        chr.commands = {
          { 'right', ls },
          { 'char',  idx }
        }
      end
    end
  end
  fonts.constructors.features.otf.register {
    name         = 'mathletterspace',
    description  = 'add math letterspacing',
    default      = 20,
    initializers = {
      base = mathletterspace,
      node = mathletterspace,
      plug = mathletterspace,
    },
  }
end
}

\begin{document}     

\font\f="DejaVuSerif-Italic:mathletterspace=20" at 10pt
\f

\hfill abcdefxyz\hfill\hbox{}

\hfill$\textfont1=\f abcdefxyz$\hfill\hbox{}

\hfill$\textfont1=\f\displaystyle abcdefxyz$\hfill\hbox{}

$$\textfont1=\f abcdefxyz$$

$$\textfont1=\f\textstyle abcdefxyz$$

\end{document}

But this does not work since the width field is nil. Any ideas how to fix this? (I'm not really familiar with luaotfload and lua in general.)

2 Answers 2

1

As Marcel said in your bug report: You need manipulators, not initializers. And there the value is the third argument (I used a larger value to make the effect more visible):

\documentclass{article}

\directlua{
do
  local function mathletterspace(tfmdata,_, value)
    local ls = tonumber (value)
    if not ls then
      luaotfload.log.report ("both", 0, "letterspace",
                 "Invalid argument to letterspace.")
      return
    end
    if tfmdata.parameters.factor then
      ls = ls * tfmdata.parameters.factor
    end

    for idx,chr in next, tfmdata.characters do
      if chr and chr.width then 
        chr.width = chr.width + 2*ls
        chr.commands = {
          { 'right', ls },
          { 'char',  idx }
        }
      end
    end
  end
  fonts.constructors.features.otf.register {
    name         = 'mathletterspace',
    description  = 'add math letterspacing',
    default      = 20,
    manipulators = {
      base = mathletterspace,
      node = mathletterspace,
      plug = mathletterspace,
    },
  }
end  
}

\begin{document}

\font\f="DejaVuSerif-Italic:mathletterspace=200" at 10pt
\f

\hfill abcdefxyz\hfill\hbox{}

\hfill$\textfont1=\f abcdefxyz$\hfill\hbox{}

\hfill$\textfont1=\f\displaystyle abcdefxyz$\hfill\hbox{}

$$\textfont1=\f abcdefxyz$$

$$\textfont1=\f\textstyle abcdefxyz$$

\end{document}

enter image description here

3
  • Thanks, that seems to work, except that my code is still called twice (and the width thus increased by too much). Also, is the difference between initializers and manipulators documented somewhere? Also, what's the difference between base, node, and plug?
    – Achim
    Commented Sep 26, 2022 at 12:50
  • Okay, I found out why my code is called twice: it's once for the default font (cmr) and once for the font I actually specify. Any idea of how to prevent this?
    – Achim
    Commented Sep 26, 2022 at 16:32
  • I figured it out, it was the default value.
    – Achim
    Commented Sep 26, 2022 at 16:45
0

Just for posterity: the following code seems to work fine.

\directlua{
do
  local function sidebearing(tfmdata, _, value)
    local sb = tonumber (value)
    if not sb then
      luaotfload.log.report ("both", 0, "letterspace",
                             "Invalid argument to letterspace (not a number).")
      return
    end
    if sb == 0 then
      return
    end
    sb = sb * tfmdata.parameters.factor

    for idx,chr in next, tfmdata.characters do
      if chr then
        chr.width    = chr.width + 2 * sb
        chr.commands = {
          { 'right', sb  },
          { 'char',  idx }
        }
      end
    end
  end

  fonts.constructors.features.otf.register {
    name         = 'sidebearing',
    description  = 'additional sidebearing',
    default      = 0,
    manipulators = {
      base = sidebearing,
      node = sidebearing,
      plug = sidebearing,
    },
  }
end
}

You must log in to answer this question.

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