latex3 / luaotfload

OpenType font loader for LuaTeX
Other
56 stars 6 forks source link

letter spacing in display math #238

Closed blumens closed 1 year ago

blumens commented 1 year ago

Using fonts with additional letter spacing does not seem to work in display formulae.

Example (LuaHBTeX, Version 1.15.0):

\documentclass{minimal}
\begin{document}
\font\f="DejaVuSerif-Italic:letterspace=20.0" at 10pt
\f

% these work as expected

\hfill abcdefxyz\hfill\hbox{}

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

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

% these do not

\[\textfont1=\f abcdefxyz\]

\[\textfont1=\f\textstyle abcdefxyz\]

\end{document}
zauguin commented 1 year ago

It's somewhat unfortunate that this is inconsistent between modes, but letterspacing gets applied to toplevel inline math as a side effect. I don't think that letterspacing conceptually makes sense for a math font and doing it anyway would break TeX's assumptions about the width of formulas.

What is your actual use case?

blumens commented 1 year ago

In math mode TeX adds italic correction to do some pseudo-letterspacing. (The difference between normal math and \mathit.) For fonts without italic correction (e.g. almost all OTF fonts), using a fixed letterspacing provides a workaround. In fact, I prefer to do it that way since the spacing is less uneven than in the italic correction based version.

It is my opinion that letterspacing is required for decent math typesetting.

zauguin commented 1 year ago

For fonts without italic correction (e.g. almost all OTF fonts)

Are you actually using a math font or are you trying to use text fonts for math? Every OpenType math font should contain italic correction.

Anyway it seems like you mostly just want to add some fixed width to all letters, right? Then you can just patch the font.

blumens commented 1 year ago

As far as I know there are very few actual OTF math fonts, none of which I like. I therefore want to use other fonts. For instance, currently I'm working with Minion Pro and Adobe Jenson.

Patching fonts might work for me privately (although I would have to check whether FontForge has a corresponding option to do so automatically), but won't work if I distribute my packages. Also it has the disadvantage that I need two versions of the font: one for text and one for math. Finally, patching a commercial font might not be allowed by its license.

Thinking some more about it, instead of adding letterspacing, it would be sufficient if luaotfload had a similar option to set the italic correction of all characters in a font some a fixed value.

zauguin commented 1 year ago

Patching fonts might work for me privately (although I would have to check whether FontForge has a corresponding option to do so automatically), but won't work if I distribute my packages. Also it has the disadvantage that I need two versions of the font: one for text and one for math. Finally, patching a commercial font might not be allowed by its license.

Thinking some more about it, instead of adding letterspacing, it would be sufficient if luaotfload had a similar option to set the italic correction of all characters in a font some a fixed value.

With patching the font I meant dynamically modifying the metrics in LuaTeX and not actually patching the font file. Basically it's the same mechanism for the italic correction or the width: Define a font feature and mess with the table a bit. See e.g. https://tex.stackexchange.com/a/655392/80496 and then just iterate over the whole font instead of changing a single letter.

blumens commented 1 year ago

That seems to work, thanks. (Haven't tried it yet, though.)

Perhaps the letterspace feature could always be implemented that way in luaotfload (using a virtual font as in the stackexchange post)? That would be the most robust version, wouldn't it?

By the way, is this interface (tfmdata, etc.) somewhere documented? I haven't found anything in the luatex manual or the luaotfload documentation.

blumens commented 1 year ago

Please ignore my question regarding the documentation. I've found it.

Speaking of documentation, if luaotfload's behaviour regarding the letterspace feature is not changed, I suggest to add a remark to the documentation that the feature is not supported in math mode.

zauguin commented 1 year ago

Perhaps the letterspace feature could always be implemented that way in luaotfload (using a virtual font as in the stackexchange post)? That would be the most robust version, wouldn't it?

The letterspace feature is much more context sensitive than these local changes to characters. E.g. it allows context sensitive overwrites, drops the additional space next to non glyphs and interacts properly with discretionaries to not kern against the margin. Also accent placement would fail if we manipulated the width of characters without taking the shaping into account.

blumens commented 1 year ago

So, here is what I have so far:

`\documentclass{minimal}

\directlua{ do local function mathletterspace(tfmdata, value) local ls = tonumber (value) if not ls then logreport ("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 unfortunately, it does not work. (Perhaps it's a simple mistake. I never have written Lua code before.) I checked that my code gets called (twice actually, not sure why), but the width field of each character is nil.

By the way, we can move this discussion to stackexchange, if you think it's not appropriate here.

zauguin commented 1 year ago

but the width field of each character is nil.

That's pretty much the same issue the StackExchange question is asking. See the answer for how to fix it. (Passing the number in is more complicated though since the manipulator doesn't get it as an argument, so you have to store it. It's probably easier to start with a fixed value, but feel free to ask a StackExchange question if you want more details on this.)