TransWikia.com

Fixing the position of math accents when using unicode-math in lualatex with a text font for letters and digits

TeX - LaTeX Asked on December 16, 2020

for a document, I am forced to use an otf font that has no math companion. For consistency reasons, I thus need to use my text font for operators and digits. So far I have:

setmainfont{Source Serif Pro}
setsansfont{Source Sans Pro}[Scale=MatchLowercase]
setmonofont{Source Code Pro}[Scale=MatchLowercase]
setmathfont{Erewhon Math}[Scale=1.1]
setmathfont[range=up]{Source Serif Pro}
setmathfont[range=it]{Source Serif Pro Italic}

which is not bad (at least for what I need to do). I am getting lots of complaints from fontspec about missing Math script in the fonts and lack of font features (e.g. Style=MathScript) but the typesetting seems more or less OK. My sole issue is with math accents that get badly misplaced.

For instance, consider this MWE:

documentclass{article}
usepackage{unicode-math}
setmainfont{Source Serif Pro}
setsansfont{Source Sans Pro}[Scale=MatchLowercase]
setmonofont{Source Code Pro}[Scale=MatchLowercase]
setmathfont{Erewhon Math}[Scale=1.1]
setmathfont[range=up]{Source Serif Pro}
setmathfont[range=it]{Source Serif Pro Italic}

begin{document}
$hat V$
end{document}

When compiled via LuaLaTeX, the accent on top of the V is way to much on the right.

Is it possible to somehow manually ‘inject’ info about the accent positioning? Even going down to a lua script if needed…

One Answer

You can set custom top_accent values by adding a font feature and patching the font table, but by default this will be ignored because you aren't loading a math font. So additionally, you'll have to declare your font a mathfont. Then, it still won't work because LuaTeX considers the font a legacy mathfont and therefore doesn't apply the OpenType mathfont based top_accent value. So you have to emulate being an OpenType mathfont. This requires you to set at least one math parameter. That's a bit problematic becuase this will overwrite the corresponding parameters from your primary mathfont, so you'll have to reload you primary mathfont later to fix up the parameters again.

A possible approach would be (comments inline)

documentclass{article}
usepackage{unicode-math}

% First some catcode setup, not really important
begingroup
  longdefx#1{directlua{unexpanded{#1}}}
  catcode`#=12 catcode`%=12
  expandafterendgroupx{
  --[[Declare a helper DeclareTopAccent to invoke the package later]]
  local id = luatexbase.new_luafunction'DeclareTopAccent'
  local mappings = {}
  token.set_lua('DeclareTopAccent', id)
  lua.get_functions_table()[id] = function()
    --[[This is executed when the command is called. We have to parse the input. Take a peek at the usage of DeclareTopAccent below before trying to read the code, then it should be relativly easy to follow]]
    local t = {}
    repeat
      local cp = assert(token.scan_int(), 'No codepoint found')
      token.scan_keyword'='
      t[cp] = assert(token.scan_int(), 'No offset found')
    until not token.scan_keyword';'
    assert(token.scan_token().cmdname == 'relax', 'Final delimiter missing')
    --[[Save the parsed mapping in a global table and then send the index back to TeX]]
    mappings[#mappings+1] = t
    tex.sprint(string.format("top_accents_id=%i", #mappings))
  end

  --[[Now implement the feature. Nothing particularly interesting here, it's 
    the same as almost any use of otf.register: Take the feature value, do some lookups, apply to characters]]
  fonts.constructors.features.otf.register {
    name = 'top_accents_id',
    description = 'Change selected top_accent values',
    initializers = {
      base = function(tfmdata, value, features)
        local mapping = assert(mappings[value], "I'm going to strike")
        local characters = tfmdata.characters
        for cp, top_accent in next, mapping do
          --[[For some reason some properties have different names here than the native LuaTeX name. E.g. top_accent is just accent]]
          assert(characters[cp], 'Why are you doing this to me?').accent = top_accent
        end
        --[[For top_accent to have any effect, LuaTeX must consider this a modern Math font.
          To fake that, we'll provide a dummy parameter if necessary. MinConnectorOverlap = 0 should be relativly safe.
          Remember to reset your math parameters after loading this font]]
        if not tfmdata.mathparameters or not next(tfmdata.mathparameters) then
          tfmdata.mathparameters = { MinConnectorOverlap = 0 }
        end
      end,
    },
  }
}

setmainfont{Source Serif Pro}
setmathfont{Erewhon Math}[Scale=1.1]
% Example of usage: DeclareTopAccent expects pairs of codepoint and top_accent value,
% separated by ; and terminated with relax.
% The unit of top_accent is milli-em, so in most cases values around 500 are a good starting point.
setmathfont[range=it, RawFeature={DeclareTopAccent `W=550;`V=450relax}]{Source Serif Pro Italic}
% Reset Math parameters messed up by the other fonts
setmathfont{Erewhon Math}[Scale=1.1, range={}]

begin{document}
$hat V$
$hat W$
end{document}

enter image description here

But while this is possible, it is a lot of work to adapt these for all characters and you still don't get a font as good as a proper math font. You should really try to switch to an OpenType math font as soon as possible.

Answered by Marcel Krüger on December 16, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP