The same three-line preamble compiles under LuaLaTeX and dies under XeLaTeX with ! Package fontspec Error: The font "TeX Gyre Termes" cannot be found. Nothing is broken; that is fontspec working as designed. fontspec is the package that lets LaTeX call a font by its name, so a single \setmainfont line changes the body typeface. But who gets asked for that name differs between engines, and that difference is the one thing that actually breaks people’s documents. This page puts that name-resolution split at the centre and works outward through \setmainfont, \newfontfamily, file-based selection and the OpenType feature keys — with measurements for each.
Which engines fontspec runs on, and why pdfLaTeX is not one of them
fontspec runs on exactly two engines, XeLaTeX and LuaLaTeX; under pdfLaTeX it fails on the \usepackage{fontspec} line itself. The reason is historical. The TeX of the 1980s knows only its own TFM metrics and Type 1 / METAFONT fonts. OpenType, and the idea of a system-wide font database, are things the world built after TeX existed. XeTeX and LuaTeX are engines rebuilt from the outside to meet that world, handling Unicode input and OpenType fonts natively. fontspec is the layer that translates those newer abilities into LaTeX vocabulary — and pdfTeX has no such abilities to translate.
Loading it is the single line \usepackage{fontspec}. Classes and packages aimed at XeLaTeX and LuaLaTeX (bxjsarticle, unicode-math, luatexja-fontspec and friends) usually load it for you first, and writing it again explicitly does no harm. When you port a document over from pdfLaTeX, remember to remove \usepackage[T1]{fontenc} and \usepackage[utf8]{inputenc}: the first is redundant because fontspec moves you to the TU encoding, and the second means nothing on a Unicode engine.
Changing the body font with \setmainfont, \setsansfont, \setmonofont
Three commands carry a document: \setmainfont{…} (the roman face, the default body font), \setsansfont{…} (what \textsf and \sffamily select), and \setmonofont{…} (the monospaced face behind \texttt and \ttfamily). The argument is a typeface name or a font filename. From it fontspec hunts down the whole quartet — upright, bold, italic, bold italic — and wires it to \textbf, \textit and \emph, so nothing about how you write the body changes. Assembling that quartet automatically is fontspec’s central job, and as we will see, you only ever write extra configuration when the automation fails.
% compile with lualatex (or xelatex — see the next section)
\documentclass{article}
\usepackage{fontspec}
\setmainfont{TeX Gyre Termes} % roman / body
\setsansfont{TeX Gyre Heros} % sans serif
\setmonofont{TeX Gyre Cursor} % monospaced
\begin{document}
Body text, \textbf{bold}, \textit{italic}, \textsf{sans}, \texttt{mono}.
\end{document}As a rule these belong in the preamble, for one reason: LaTeX freezes its math fonts at \begin{document}. Run \setmainfont in the preamble and the same face also reaches the roman letters inside maths, such as \mathrm; run it in the body and only the text changes, leaving math’s roman behind. The math typeface itself is outside fontspec’s remit — that belongs to \setmathfont from unicode-math. Remember that text and maths are driven by two different commands.
The font "…" cannot be found. — XeLaTeX and LuaLaTeX look in different places
If ! Package fontspec Error: The font "…" cannot be found. appears only under XeLaTeX, it is not a typo in the name — it is a difference in who gets asked. XeTeX queries the operating system’s font database, so it can see only typefaces the OS considers installed. LuaTeX builds its own index through luaotfload, and that index covers the TeX tree as well. Fonts that TeX Live ships are not registered with the OS, so that gap becomes the gap in results. On this machine’s TeX Live 2024, an identical file compiled under LuaLaTeX and stopped under XeLaTeX.
$ xelatex t1.tex
! Package fontspec Error: The font "TeX Gyre Termes" cannot be found.
$ lualatex t1.tex
Output written on t1.pdf (1 page, 3697 bytes).
$ pdffonts t1.pdf
name type encoding emb sub uni
NNOCLB+TeXGyreTermes-Regular CID Type 0C Identity-H yes yes yesThe line is drawn between “bundled font” and “OS font.” Measured on this machine, typefaces installed on the system side — Hiragino Mincho ProN, Hiragino Sans, YuMincho — do resolve under XeLaTeX. Fonts that TeX Live distributes under texmf-dist/fonts/opentype/public/, by contrast, are invisible to XeLaTeX by name; in TeX Live 2024 that directory holds 125 packages (1,499 actual .otf files). The engines’ own design logic belongs to /learn/engines/xetex and /learn/engines/luatex. What you need here is the fix that gets a broken document compiling again.
There are two fixes. (1) Compile with lualatex. It needs no source change at all, so in most cases it is the shortest route. (2) Write the filename instead of the typeface name. \setmainfont{texgyretermes-regular.otf} does work under XeLaTeX — but at a price: named by file, fontspec can no longer guess the bold and italic members. Use \textbf and you get LaTeX Font Warning: Font shape 'TU/texgyretermes-regular.otf(0)/b/n' undefined, and the passage you meant to be bold comes out light. This is the classic silent failure: a warning, not an error, so it slips past. Filenames are case-sensitive, too — write TeXGyreTermes-Regular.otf and you are back to cannot be found for the very same font.
Selecting by file: Path=, Extension=, BoldFont=
The complete file-based form assigns all four styles by hand. Give the directory with Path= and the suffix (.otf or .ttf) with Extension=, then map each file onto UprightFont=, BoldFont=, ItalicFont=, BoldItalicFont=. The asterisk * inside those values is a placeholder that is replaced by the common base name from the first argument. The bold and italic that vanished in the previous section come back the moment you write these four: measured here, the four-way version produced zero Font shape … undefined warnings, and pdffonts listed Regular, Bold, Italic and BoldItalic side by side.
\usepackage{fontspec}
% XeLaTeX-safe: name the files, and name all four styles
\setmainfont{texgyretermes}[
Extension = .otf ,
UprightFont = *-regular ,
BoldFont = *-bold ,
ItalicFont = *-italic ,
BoldItalicFont = *-bolditalic ,
]
% fonts shipped inside the project directory
\setsansfont{LibreBaskerville}[
Path = ./fonts/ ,
Extension = .otf ,
UprightFont = *-Regular ,
BoldFont = *-Bold ,
]Which form to choose comes down to a practical question: who is going to typeset this document? For a draft only you will ever read, the typeface name is quicker. When the same PDF has to come out on someone else’s machine — co-authors, journal submission, CI, Docker — bundling the fonts inside the project and pointing Path= at them is close to the only reliable answer. Given Path=, fontspec looks straight at that relative path rather than TeX’s search path, so the whole repository travels together.
| Method | How | When it suits | Reaches bundled fonts on XeLaTeX? |
|---|---|---|---|
By name | \setmainfont{TeX Gyre Termes} | Drafts and personal docs; bold and italic pair up automatically | No — only typefaces registered with the OS |
By file, upright only | \setmainfont{texgyretermes-regular.otf} | When you just need it to compile; you lose bold | Yes — but \textbf silently stays light |
By file, all four | Path / Extension / UprightFont / BoldFont / ItalicFont / BoldItalicFont | Collaboration, submission, CI, custom fonts — whenever reproducibility comes first | Yes — and no Font shape … undefined warnings |
Adding a fourth and fifth typeface with \newfontfamily
A typeface that does not fit the roman/sans/mono trio is added by creating your own switch with \newfontfamily. The first argument is the command name you want, the second the font, and it accepts the same options as \setmainfont. The command it creates is declarative, like \rmfamily: it applies to everything that follows. Limit its reach by wrapping it in braces, {\titlefont …}.
\usepackage{fontspec}
\newfontfamily\titlefont{TeX Gyre Bonum}
\newfontfamily\quotefont{TeX Gyre Schola}[Scale = MatchLowercase]
% ...
\titlefont A heading in Bonum
{\quotefont A quoted passage in Schola.}For a face used once, you can skip the definition and apply \fontspec{Font Name} as a declaration on the spot. But \fontspec reloads the font on every call, so using it for something recurring — chapter headings, say — quietly inflates compile time. Two uses or more: define it with \newfontfamily. Note also that \newfontfamily fails if a command of that name already exists, so avoid short names LaTeX already owns, like \sc or \it, and pick something that says what it is for, such as \titlefont.
Turning on OpenType features: Ligatures, Numbers, Scale
An OpenType font arrives with alternate glyphs already inside it — ligatures, figure variants, small capitals — and fontspec pulls them out through key=value options. The effects are quicker to measure than to eyeball, so here are numbers taken from TeX Gyre Pagella at 10pt. Apply Numbers=OldStyle and the depth of 0123456789 grows from 0.19998pt to 2.37999pt: the fact that old-style figures give 3 and 9 a descender shows up directly in the measurement.
Ligatures=— ligatures and input conversion.Ligatures=TeXturns on the TeX way of typing:--becomes an en dash,---an em dash, `and''become curly quotes. (Measured:---goes from 9.99pt of “three hyphens” to exactly 10.0pt — one em — as a real em dash.) Other values areCommon(the standard fi, fl, …),NoCommonto switch them off (measured:fiwidens from 6.05pt to 6.35pt),Rare/Discretionary, andHistoric`.Numbers=— figure style. CombineOldStyle(text figures that sit inside prose) orLining(uniform-height modern figures) withProportionalorMonospaced. Figures inside a table line up vertically once you setLining,Monospaced.Letters=SmallCaps— real small capitals. Measured on Libertinus Serif,Abcdrops from 6.98pt to 6.58pt tall and grows from 16.26pt to 16.94pt wide. Watch out: if the font has nosmcptable,fontspecdoes nothing and says nothing — on TeX Gyre Pagella the width stayed at 17.6pt. Values such asLetters=Uppercaseexist too.SmallCapsFeatures=— a bundle of features to apply only in small caps, for instanceSmallCapsFeatures={Numbers=OldStyle}. The same pattern gives youUprightFeatures=,BoldFeatures=andItalicFeatures=, so each style can be configured separately.Scale=— the scale factor: a number, orScale=MatchLowercase(match the x-height of the main font) orScale=MatchUppercase(match the cap height). Measured, TeX Gyre Heros’s x-height of 5.24pt came down to 4.68999pt against Pagella body text — exactly Pagella’s 4.69pt.StylisticSet=— selects, by number, a stylistic set of alternates the font provides.RawFeature=is the escape hatch for features with no dedicated key, passing an OpenType tag straight through;RawFeature={+onum}gives the same result asNumbers=OldStyle(measured: the same 2.37999pt depth).Script=/Language=— choose the writing system and language (Script=Arabic,Language=Turkish, and so on).Renderer=picks the shaping engine; under XeTeX you can chooseAAT,OpenTypeorGraphite. Under LuaTeX the work is done byluaotfload, so the key is rarely needed.
\usepackage{fontspec}
\setmainfont{TeX Gyre Pagella}[
Ligatures = TeX ,
Numbers = OldStyle ,
SmallCapsFeatures = {Numbers = Lining} ,
]
% match the sans to the body x-height
\setsansfont{TeX Gyre Heros}[Scale = MatchLowercase]To add a feature to one spot rather than the whole document, use \addfontfeature{…} (or \addfontfeatures{…} for several). It overrides the current font locally with the feature you name, taking effect only inside the enclosing braces. The canonical use: a document set in old-style figures where the numbers inside one table need to line up.
Set in old-style figures, but this column
{\addfontfeature{Numbers={Lining,Monospaced}} 01234 56789}
lines up digit by digit.What fontspec does not cover: maths and Japanese
fontspec handles only the Latin text of the body; maths and Japanese run on separate tracks. For maths you load unicode-math and hand an OpenType math font — Latin Modern Math, STIX Two Math, NewCM Math — to \setmathfont{…}. Changing \setmainfont moves the maths not at all, so a document whose text and formulas do not match is not a pdfLaTeX-only problem. Which math font to pick belongs to the math-fonts page.
Japanese has the same shape. \setmainfont changes only the Latin face; kanji and kana do not move. On LuaLaTeX you load luatexja-fontspec — effectively the Japanese counterpart of fontspec — and use its “j” commands: \setmainjfont{…} (Mincho, the Japanese body face), \setsansjfont{…} (Gothic), \newjfontfamily. The option syntax is essentially the same, so what you know about Scale= and Path= carries straight over. Japanese typesetting on a Unicode engine therefore means running two tracks in parallel. Which Japanese fonts are available is covered on the Japanese-fonts page.