[Tex/LaTex] How to keep a “rare” ligature from interfering with a “common” ligature

fontsfontspecligaturesluatexxetex

In ligature-rich Opentype fonts which feature both "common" and "rare" ligatures, it can happen that a "rare" ligature is applied first to one character pair, pre-empting the use of a "common" or "rare" ligature that ought to be applied to the preceding or trailing character pair. (Regarding ligature terminology in Opentype fonts: Most, but not all, Opentype font families I'm familiar with classify the f-ligatures — ff, fi, fl, ffi, ffl, and possibly also ft, fft, and a few more — as "common" and all others as either "rare"/"discretionary" or "historic". )

Two examples of this behavior, which can arise with both XeLaTeX and LuaLaTeX:

  • With the font Garamond Premier Pro, the "rare" is ligature is applied first to words such as fish and fist, pre-empting the use of the "common" fi ligature.

  • With the font Junicode, the "rare" iv and ix ligatures are applied first to words such as five, fix, and affixed, pre-empting the use of the "common" fi and ffi ligatures.

enter image description here

Without this pre-emption, the output would look like this (achieved the "brute force" way, i.e., by inserting \kern0pt between i and s, i and v, and i and x, respectively):

enter image description here

(Observe, by the way, that the brute-force suppression of the rare is ligature in the word "fist" not only enables the common fi ligature but also the rare st ligature.)

Question: Which methods may be available — ideally on a document-wide basis — that prevent the application of certain rare ligatures if they pre-empt other, either common or rare, ligatures?

Aside: This posting is not a duplicate of the earlier posting How to suppress a "Rare" ligature that pre-empts a "Common" ligature for the same character pair? In the earlier question, the issue was that "rare" and "common" forms of a given ligature could be present for one and the same character pair (or triple). Examples of this issue are common and rare forms of ligatures for "ft" and "fft".


Here's the code that gives rise to the second screenshot shown above; remove the \kern0pt directives to generate the first screenshot:

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{fontspec}
\begin{document}

\setmainfont[Ligatures  = Rare,
             ItalicFont = {Garamond Premier Pro Italic}]           
            {Garamond Premier Pro}

% 'is'-ligature available only in italic shape in GPP
\emph{fi\kern0ptsh fi\kern0ptst} --- Garamond Premier Pro

\setmainfont[Ligatures = Rare]{Junicode}

fi\kern0ptve fi\kern0ptx affi\kern0ptx --- Junicode

\end{document}

Best Answer

In my view, the following ligature suppression rules are needed.

  • In order to enable the fi ligature, suppress the is, iv, and ix ligatures if is/iv/ix is preceded by f;

  • In order to enable the sp and st ligatures, suppress the is ligature if is is followed by either p or t.

In the jargon of Opentype fonts, fi is called a common ligature, and is, iv, and ix, as well as sp and st, are called rare or discretionary ligatures.

The following solution, which works under LuaLaTeX (but not under XeLaTeX), employs the selnolig package. (Full disclosure: I'm the main author of the selnolig package.) The ligature suppression rules given above are implemented via several \nolig instructions:

\nolig{fis}{fi|s} % break up 'is' lig. if preceded by 'f'
\nolig{fiv}{fi|v} % break up 'iv' lig. if preceded by 'f'
\nolig{fix}{fi|x} % break up 'ix' lig. if preceded by 'f'
\nolig{isp}{i|sp} % break up 'is' lig. if followed by 'p'
\nolig{ist}{i|st} % break up 'is' lig. if followed by 't'

More concisely, one would write:

\nolig{fi[svx]}{fi|.}
\nolig{is[pt]}{i|s.}

The . character after the | is shorthand for "any glyph". I'm not sure if the increase in terseness and density is worth the cost of reduced (human) legibility.

If one wanted to suppress the is, iv, and ix ligatures globally, while still allowing other "rare" ligatures to be used, a single \nolig instruction would do the job:

\nolig{i[svx]}{i|.}

In the following code, the upright shape of Junicode is used for the iv and ix cases, and the italic shape is used of Garamond Premier Pro is used for the sp and st ligatures. The first data row shows the outcome with selnolig being "on"; for comparison, the second data row shows the outcome with selnolig being turned "off".

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{fontspec,booktabs}
\setmainfont[Ligatures  = Rare,
             ItalicFeatures = {Scale = MatchLowercase},
             ItalicFont = {Garamond Premier Pro Italic}]
            {Junicode}
\setsansfont[Scale=MatchLowercase]{Palatino Sans Com}

\usepackage{selnolig}
\nolig{fis}{fi|s} % break up 'is' lig. if preceded by 'f'
\nolig{fiv}{fi|v} % break up 'iv' lig. if preceded by 'f'
\nolig{fix}{fi|x} % break up 'ix' lig. if preceded by 'f'
\nolig{isp}{i|sp} % break up 'is' lig. if followed by 'p'
\nolig{ist}{i|st} % break up 'is' lig. if followed by 't'

%% Or, more concisely:
% \nolig{fi[svx]}{fi|.}
% \nolig{is[pt]}{i|s.}

\begin{document}

\noindent
\begin{tabular}{@{}lll@{}}
\textsf{selnolig} & Junicode: iv, ix  & \em Gar.\ Prem.\ Pro Italic: is\\
\midrule
On & five fix affix & \em fish fissure fist gist lisp\\[0.5ex]
\selnoligoff % turn off selnolig's operations
Off & five fix affix & \em fish fissure fist gist lisp\\
\end{tabular}
\end{document}
Related Question