fontspec icon indicating copy to clipboard operation
fontspec copied to clipboard

Changing features disables kerning for the same font file

Open svenper opened this issue 5 years ago • 8 comments

Description

If a font has kerning between glyphs accessed through features, the kerning is not applied if the glyphs are separated by a feature change.

In the MWE the superscript (U+2070) has kerning that can be removed, making the f and touch, which is what always happens with the sups figure 0.

Check/indicate

  • [x] Relevant for XeTeX
  • [x] Relevant for LuaTeX
  • [x] Issue tracker has been searched for similar issues?
  • [ ] Links to <tex.stackexchange.com> discussion if appropriate

Minimal example demonstrating the issue

\documentclass{article}
\usepackage{fontspec}
\setmainfont{SourceSerifPro-Regular.otf}
% \setfontfamily\garamond{EBGaramond12-Regular.otf}

\begin{document}

f⁰
{\addfontfeature{Kerning=Off}f⁰}
f{\addfontfeature{VerticalPosition=Superior}0}

% \garamond
% {\addfontfeature{Letters=SmallCaps}Th}
% T{\addfontfeature{Letters=SmallCaps}h}

\end{document}

f

th

Further details

The workaround to somehow apply the feature before the kerning pair is not always feasible (e.g. changing the footnote style), that in ture wouldn't even work with Source Serif as its sups feature also raises alphabet glyphs.

sups 0 and use the same glyph zero.sups, as seen in FontForge metrics for /f/zero/f/zero.sups/f/uni2070. The same issue can be seen in EB Garamond 12 with smcp Th.

svenper avatar May 18 '19 01:05 svenper

Thanks for writing.

Unfortunately there is almost nothing that fontspec can do about this — loading the font with a different feature makes it a different font, and kerning between fonts is not possible.

The only approach I can think of in fontspec to assist would be some helper macros like the following, where X and Y are fontspec features and A and B are text to typeset, and K is inserted between them:

\affinstance{X}{A}{Y}{B} % for use in the document

\affsetup{pattern X}{pattern A}{\K}{pattern Y}{pattern B} % in the preamble

where \affinstance typesets

{\addfontfeature{X} A}\K{\addfontfeature{Y} B}

And regexp could be used for retrieving the appropriate "insert" \K. E.g., in your example we might define in the preamble

\addsetup{}{.*}{\kern-0.1pt}{VerticalPosition=Superior}{.*}

and then in the document you would have to write

\affinstance{}{f}{VerticalPosition=Superior}{0}

resulting in

{f}\kern-0.1pt{\addfontfeature{VerticalPosition=Superior}0}

Does this look like it could be of any help?

If I do anything like this, it won't be in the short term, but I'll leave this open here for further discussion.

wspr avatar May 18 '19 03:05 wspr

I can't think of many other cases (let alone any common ones) where feature changes would need kerning (maybe character variants in some calligraphic font, or ital feature).

For my specific problem however, a workaround in LuaTeX would be to replace the glyphs by replacing the characters instead of the features. One hack is more than enough; this seems like it should be fixed in the kernel anyway, unless that would break some compatibility?

\documentclass{article}
\usepackage{luacode}
\usepackage{fontspec}
\setmainfont{SourceSerifPro-Regular.otf}

\begin{luacode*}
        function luasuperhack ( s )
                s = unicode.utf8.gsub ( s, "0", "⁰" )
                s = unicode.utf8.gsub ( s, "1", "¹" )
                s = unicode.utf8.gsub ( s, "2", "²" )
                s = unicode.utf8.gsub ( s, "3", "³" )
                s = unicode.utf8.gsub ( s, "4", "⁴" )
                s = unicode.utf8.gsub ( s, "5", "⁵" )
                s = unicode.utf8.gsub ( s, "6", "⁶" )
                s = unicode.utf8.gsub ( s, "7", "⁷" )
                s = unicode.utf8.gsub ( s, "8", "⁸" )
                s = unicode.utf8.gsub ( s, "9", "⁹" )
                tex.sprint ( s )
        end
\end{luacode*}
\newcommand{\superhack}[1]{\directlua{luasuperhack(\luastring{#1})}}

\makeatletter
\renewcommand*{\@footnotemark}{\superhack{\@thefnmark}}
\makeatother

\begin{document}

f⁰
f\superhack{0}
f\footnote[0]{test}

\end{document}

svenper avatar May 18 '19 16:05 svenper

That’s a good approach here. If further flexibility is needed, it’s not out of the question that you could construct your own “virtual font” in LuaTeX. (Although I don’t have code lying around to show how to do that.)

wspr avatar May 19 '19 01:05 wspr

It is possible for harf to allow changing font features without switching fonts, but I’m not clear what the interface would look like. I believe that should be doable for luaotafload as well.

I imagine something like attributes can be used for this, but attributes are numbers only. There is also properties which are more general, but I can’t see a way to set them from TeX side.

khaledhosny avatar May 24 '19 14:05 khaledhosny

I think one can define a new font feature to switch to the superscripts without changing the font. In the example I used ! as a trigger, but some other char should work too. I'm in no way sure if the lookup rules are really correct - there is no documentation available, I had only a few context examples and so it is more or less guess and try. I only add code for 0-4, the rest of the digits are missing.

\documentclass{article}
\usepackage{fontspec}
\directlua{
 fonts.handlers.otf.addfeature {
        name    = "numsups",
        type    = "chainsubstitution",
        lookups = {
            {
             type = "ligature",
              data = {
                    ['zero.sups'] = { "!", "0" }, 
                    ['one.sups']  = { "!", "1" },
                    ['two.sups']  = { "!", "2" },
                    ['three.sups']= { "!", "3" }, 
                    ['four.sups'] = { "!", "4" },
                },
            },
            {
             type = "substitution",
              data = {
                    ["zero"]   = "zero.sups",
                    ["one"]    = "one.sups",
                    ["two"]    = "two.sups",
                    ["three"]  = "three.sups",
                    ["four"]   = "four.sups",
                },
            },
        },
        data = 
          {
            rules = 
             {
                {
                    before  = { {"!","zero.sups","one.sups","two.sups","three.sups","four.sups" } },
                    current = {  { "0", "1", "2", "3", "4" } },
                    lookups = { 2 },
                },
                {
                    current = { {"!"} },
                    lookups = { 1 },
                },
             }
          }
        }
     }


\setmainfont{SourceSerifPro-Regular.otf}[RawFeature=+numsups;]
 
\begin{document}
f⁰ !11234 123456

f!2134 f!01234 f!43222 01234

f{\addfontfeature{VerticalPosition=Superior}01234}
\end{document}

image

u-fischer avatar Jun 16 '19 13:06 u-fischer

Ideally we would need a more general solution than this, for example of one wants to enable a cvXX feature for just one character in the text string without breaking other features.

khaledhosny avatar Jun 17 '19 02:06 khaledhosny

I ran into this issue recently when using an all caps font which makes extensive use of swashes. The swashes are encoded in the "swsh", and the "cv01...cv04" features. They are typically used only in word-initial or word-final position. So I apply the feature only to the first or last letter and not to the whole word, otherwise other letters of the word turn into swashes as well which is not desired. But this breaks kerning.

For reference, my original question on stackexchange: https://tex.stackexchange.com/questions/576819/why-is-kerning-not-applied-when-mixing-opentype-character-variants-swashes-wit

lvcivs avatar Jan 01 '21 10:01 lvcivs

@lvcivs As said in the issue here and on tex.sx there is not much fontspec can do. Better open a feature request for the font. As it is an all caps font, it could e.g. add the swashes only to the uppercase letters, then you could input Barry.

u-fischer avatar Jan 01 '21 13:01 u-fischer