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
\long\def\x#1{\directlua{\unexpanded{#1}}}
\catcode`\#=12 \catcode`\%=12
\expandafter\endgroup\x{
--[[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=450\relax}]{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}
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.
\skew
.\skewchar
, which LuaTeX still uses by default when an accentee lacks atop_accent
value.