With the advent of MacTeX2014 and continuing in MacTeX2015 -- and likely to persist into the indefinite future :-( -- Lua(La)TeX can no longer directly access certain font features, such as ligatures, if the font is a system font whose ligature-related properties are AAT-encoded and not OpenType-encoded. Hoefler Text
, Didot
, and Baskerville
are three such fonts. (In contrast, XeLaTeX continues to be able to access ligature-related features directly.)
By setting up one more or "feature files" that spell out which ligature substitutions should be performed, it is possible to restore ligatures when working with version of LuaLaTeX more recent than version 0.77.
The output of the code shown below has the following characteristics: the first row is generated before the feature file is loaded; note that it's lacking ligatures even though the option Ligatures=Common
option was specified when the font (Hoefler Text
in this case) was loaded via a \setmainfont
statement. The second row, which is generated after the feature file is loaded via an \addfontfeature
instruction, contains all the expected ligatures.
Aside: (i) The example uses the filecontents
package to be self-contained. In practice, all one needs to do is to create the feature file "addligs.fea" once and store it in a directory that's searched by the TeX distribution. (ii) The feature file shown here features (pun intended...) quite a few ligature substitutions, since Hoefler Text
features lots and lots of ligatures. For other, less feature-rich, fonts, the "standard five" ligatures -- ff
, fi
, fl
, ffi
, and ffl
-- may be all that you need to take of.
% !TEX TS-program = lualatex
\RequirePackage{filecontents}
\begin{filecontents*}{addligs.fea}
languagesystem DFLT dflt;
languagesystem latn dflt;
# Ligatures
feature liga {
sub f f by f_f;
sub f i by f_i;
sub f j by f_j;
sub f k by f_k;
sub f l by f_l;
sub f f i by f_f_i;
sub f f l by f_f_l;
sub s t by s_t;
sub c t by c_t;
} liga;
\end{filecontents*}
\documentclass{article}
\usepackage{fontspec}
\setmainfont[Ligatures=Common]{Hoefler Text}
\newcommand\wordlist{off fit fjord Kafka fly office baffle stop act}
\begin{document}
\wordlist
\addfontfeature{FeatureFile=addligs.fea}
\wordlist
\end{document}
Addendum: Here's a slightly more sophisticated example, which uses two different feature files depending on whether the font face is upright or italic. It makes use of the ability of fontspec
to specify different features for Upright, Bold, Italic, and BoldItalic font faces. The separate treatment of the italic and non-italic cases is necessary because Hoefler Text
features additional ligatures (esp. for "sp" and "Th") just for the italic font faces.
% !TEX TS-program = lualatex
\RequirePackage{filecontents}
\begin{filecontents*}{AddligsHoeflerUpright.fea}
languagesystem DFLT dflt;
languagesystem latn dflt;
# Ligatures
feature liga {
sub f b by f_b;
sub f f by f_f;
sub f h by f_h;
sub f i by f_i;
sub f j by f_j;
sub f k by f_k;
sub f l by f_l;
sub f f b by f_f_b;
sub f f h by f_f_h;
sub f f i by f_f_i;
sub f f k by f_f_k;
sub f f l by f_f_l;
sub c t by c_t;
sub s t by s_t;
} liga;
\end{filecontents*}
\begin{filecontents*}{AddligsHoeflerItalic.fea}
languagesystem DFLT dflt;
languagesystem latn dflt;
# Ligatures
feature liga {
sub f b by f_b;
sub f f by f_f;
sub f h by f_h;
sub f i by f_i;
sub f j by f_j;
sub f k by f_k;
sub f l by f_l;
sub f f b by f_f_b;
sub f f h by f_f_h;
sub f f i by f_f_i;
sub f f k by f_f_k;
sub f f l by f_f_l;
sub c t by c_t;
sub s t by s_t;
sub s p by s_p;
sub T h by T_h;
sub a s by a_s;
} liga;
\end{filecontents*}
\documentclass{article}
\usepackage{fontspec}
\setmainfont{Hoefler Text}[
Ligatures = {Common,Rare},
ItalicFont = Hoefler Text Italic,
BoldFont = Hoefler Text Black,
BoldItalicFont = Hoefler Text Black Italic]
\newcommand\words{fb ff fh fi fj fk fl ffb ffh ffk ffl ct st, sp Th}
\begin{document}
ligatures missing:
\words\par
\textbf{\words}\par
\textit{\words}\par
\textbf{\textit{\words}}
\addfontfeature{%
UprightFeatures = {FeatureFile=AddligsHoeflerUpright.fea},
BoldFeatures = {FeatureFile=AddligsHoeflerUpright.fea},
ItalicFeatures = {FeatureFile=AddligsHoeflerItalic.fea},
BoldItalicFeatures = {FeatureFile=AddligsHoeflerItalic.fea}}
\medskip
ligatures restored/activated:
\words\par
\textbf{\words}\par
\textit{\words}\par
\textbf{\textit{\words}}
\end{document}
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".
% !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}
Best Answer
The following presents a solution for pdftex and Type 1 fonts. Since TeX does not offer any possibility to decompose a ligature into its constituent characters (this information is lost after they have been replaced by the ligature), we have to take the opposite approach: we loop over all glyph pairs in the fonts, typesetting them in a temporary box, and test, by way of etex's
\lastnodetype
, whether we have obtained a ligature. We can then write the slot numbers of the ligating characters to thelog
file and/or actually typeset the ligature and its parts, whereas the slot number of the ligature is only available with\showbox
(see below).Uncommenting the
\showbox
line will allow finding out, in a clumsy way, the slot number of the ligature, too, which will be presented in a form like this:This shows the triple ligature — (
emdash
), composed of the ligature – (endash
) and the character - (hyphen
), normally written as---
, where^^V
= 22, and^^U
= 21 (conversion rules here). Other than that, getting the slot numbers of ligature requires leaving the pdftex realm and resort to inspecting thetfm
file (see @egreg's solution), employing metapost (@AndrewKepert), or (probably) luatex.