Font system (NFSS / encoding)

The string OT1/cmr/m/n/10 in a LaTeX warning is not a code you have to crack — it is an address. LaTeX never names a font outright; it points at one with five coordinates: encoding, family, series, shape, size. The scheme is called NFSS. This page follows those five coordinates down to the source and the log file: why \bfseries asks for series bx rather than b, whether asking for bold after \fontseries{c} really lands on bc, and what the substitution warning Font shape ... undefined is actually telling you.

NFSS — a font is five coordinates

Font selection in LaTeX2e runs on the NFSS (New Font Selection Scheme), and its central claim is that any text font is completely determined by five attributes: encoding, family, series, shape, size. NFSS was designed by Frank Mittelbach and Rainer Schöpf; the copyright line on the kernel source ltfssbas.dtx goes back to 1989. Before it, in LaTeX 2.09, you could not even combine \bf and \it — nobody had separated weight and letterform into independent axes yet.

  • Encoding — the order of glyphs inside the font (which slot each input character maps to). For text the main two are OT1 (TeX text) and T1 (TeX extended text, known as Cork). Covered in detail below.
  • Family — a set of fonts sharing a letterform. Computer Modern Roman is cmr, its sans is cmss, its typewriter is cmtt; among the commercial faces, Times is ptm, Helvetica phv, Courier pcr.
  • Series — a single axis that fuses weight and width: m (medium), b (bold), bx (bold extended), c (condensed), bc (bold condensed), sb (semibold), and so on.
  • Shape — the form of the letters: n (upright), it (italic), sl (slanted), sc (small caps). Rarely, ui (an italic that is not slanted).
  • Size — the design size, a dimension such as 10pt; with no unit, pt is assumed.

The one thing that trips people up is that series fuses weight and width into a single value. In the standard scheme weights run ulub (ultra light to ultra bold) and widths ucux (ultra condensed to ultra expanded); the two are concatenated, and m is dropped — except that a single m survives when both weight and width are medium. So “bold, normal width” is b, “bold extended” is bx, “normal weight, condensed” is c, and “bold condensed” is bc. The five values written out as encoding/family/series/shape/size are a font’s formal name inside LaTeX, and \showthe\font prints whichever one is current.

log
% \showthe\font in a default article, and what the log answers:
> \OT1/cmr/m/n/10 .

% the same five values head every overfull-box report:
Overfull \hbox (121.83368pt too wide) detected at line 3
\OT1/cmr/m/n/10 Supercalifragilisticexpialidocious

% and the first four of them name a shape that had to be substituted:
LaTeX Font Warning: Font shape `T1/cmss/bx/sc' undefined
(Font)              using `T1/cmss/bx/n' instead on input line 4.

Why \bfseries asks for series bx, not b

The answer is that \bfseries does not read \bfdefault at all. The kernel’s \bfdefault really is b, but \bfseries first checks whether the current family equals \rmdefault, \sfdefault, or \ttdefault, and if it does it uses a dedicated default instead: \bfseries@rm, \bfseries@sf, or \bfseries@tt. And latex.ltx initialises all three to bx. \bfdefault is consulted only when the current family is none of those three — so as long as you type \bfseries in the body font, \bfdefault is bypassed.

latex.ltx
% TeX Live 2024, latex.ltx (the LaTeX2e kernel), abridged
\def\bfseries@rm{bx}
\def\bfseries@sf{bx}
\def\bfseries@tt{bx}

\DeclareRobustCommand\bfseries{%
  \not@math@alphabet\bfseries\mathbf
  \expand@font@defaults
  \maybe@update@bfseries@defaults
  \ifx\f@family\rmdef@ult      \fontseries\bfseries@rm
  \else\ifx\f@family\sfdef@ult \fontseries\bfseries@sf
  \else\ifx\f@family\ttdef@ult \fontseries\bfseries@tt
  \else                        \fontseries\bfdefault
  \fi\fi\fi
  \UseHook{bfseries}%
  \selectfont
}

So why is bx the default? Because of how many fonts Knuth actually cut. Count the Computer Modern metrics in TeX Live 2024 and you find that plain bold exists as exactly one file, cmb10.tfm. Bold extended, on the other hand, comes as cmbx5, cmbx6, cmbx7, cmbx8, cmbx9, cmbx10 and cmbx12 — seven design sizes. Only cmbx can set bold properly all the way down to footnotes and subscripts, so NFSS asks Computer Modern for bx.

That much is a Computer Modern quirk, but the kernel cleans up after itself. At \begin{document}, \init@series@setup runs and downgrades \bfseries@rm to b unless \rmdefault is one of cmr, cmss, cmtt, lcmss, lcmtt, lmr, lmss, lmtt. In other words bx is a legacy of Computer Modern and Latin Modern alone: the moment you switch to Times or TeX Gyre, \bfseries quietly starts asking for b. Measured, that is exactly what happens — add \renewcommand\rmdefault{ptm} and both \bfseries@rm and the resulting series come out b.

Setup in the documentSeries \bfseries requestsWhy
article (default)bx\rmdefault is cmr, so the initial \bfseries@rm survives
\usepackage{lmodern}bxlmr is also on the kernel’s Computer Modern list
\renewcommand\rmdefault{ptm}b\init@series@setup finds it off the list and lowers \bfseries@rm
\fontseries{b}\selectfontba value named at the low level does not go through the default macros
\DeclareFontSeriesDefault[rm]{bf}{sb}sbthe supported way to retarget the bold of just one meta-family

The practical upshot is clear: if you want to change what bold means, prefer \DeclareFontSeriesDefault[rm]{bf}{b} over \renewcommand{\bfdefault}{b}. The latter does work — rewriting \bfdefault makes \maybe@update@bfseries@defaults overwrite all three dedicated defaults — but it cannot target the roman alone, or the sans alone. And when you hit the classic “I asked for bold and nothing got bolder,” this mismatch is the first thing to suspect: your font may simply have no bx.

Series values merge: c plus b gives bc

Since the 2020 LaTeX2e release (\IncludeInRelease{2020/02/02} in latexrelease.sty), a series request merges instead of overwriting. Ask for bold while you are set in condensed (c) and you do not get b; you get bc, bold condensed. The table that decides this is built with \DeclareFontSeriesChangeRule, and the kernel carries over two hundred lines of such rules. Read {c}{b}{bc}{} as “when the current series is c and b is requested, go to bc; no fallback.”

latex.ltx
% the rule table (latex.ltx, from ltfssaxes.dtx), a handful of the 200-odd entries
\def\DeclareFontSeriesChangeRule#1#2#3#4{%
  \@namedef{series@#1@#2}{{#3}{#4}}}
\DeclareFontSeriesChangeRule {c}{b}{bc}{}     % condensed + bold      -> bc
\DeclareFontSeriesChangeRule {b}{c}{bc}{b}    % bold + condensed      -> bc, else b
\DeclareFontSeriesChangeRule {bx}{c}{bc}{bx}  % bold extended + cond. -> bc, else bx
\DeclareFontSeriesChangeRule {m}{b}{b}{bx}    % medium + bold         -> b,  else bx

The rules do not fire unconditionally, though. \selectfont first merges without substitution, then checks whether the resulting encoding/family/series/shape actually exists; if not, it restores the old values and repeats the merge in substitution mode. Computer Modern has no condensed cut at all, so \fontseries{c}\selectfont warns and falls back to m, and a following bold can only reach b — which is exactly why people conclude that merging “does not happen.” Try it on a family that really has condensed and the rules work. Helvetica (phv) ships m, c, b and bc in T1, so the example below shows the real thing.

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{helvet}          % sets \sfdefault to phv
\begin{document}\makeatletter
\sffamily                    % -> phv/m   (Helvetica)
\fontseries{c}\selectfont    % -> phv/c   font file phvr8tn (Helvetica Narrow)
\bfseries                    % -> phv/bc  font file phvb8tn (Helvetica Bold Narrow)
\typeout{[series] \f@series\space -> \fontname\font}
\makeatother\end{document}

% [series] bc -> phvb8tn
%
% The same document with Computer Modern instead reports:
%   LaTeX Font Warning: Font shape `OT1/cmr/c/n' undefined
%   [series] m  (the condensed request was substituted away before bold arrived)

The low-level selectors and \selectfont

Low-level selectors set one attribute at a time: \fontencoding{T1}, \fontfamily{ptm}, \fontseries{b}, \fontshape{it}, \fontsize{12}{14}. There is exactly one rule — setting them does nothing until you call \selectfont — and the previous section showed why. \fontseries does not write the value anywhere; it pushes it onto a pending list called \delayed@f@adjustment, and it is \selectfont that performs the merge, the existence check, and any substitution in one go. Forget the \selectfont between a setting and the text, and that text comes out in the previous font.

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}
\begin{document}
% Correct: set the attributes, commit with \selectfont, then the text
{\fontfamily{ptm}\fontseries{b}\selectfont Bold Times}

% Wrong: the words before \selectfont are still in the old font
{\fontfamily{ptm} Some \fontseries{b}\selectfont text.}

% \usefont gives four attributes at once and commits on the spot
{\usefont{T1}{ptm}{b}{it} Bold italic Times}

% Change only the size; reuse the current baselineskip
\makeatletter
{\fontsize{14}{\f@baselineskip}\selectfont Fourteen point}
\makeatother
\end{document}

The shortcut that passes four of the five at once is \usefont{encoding}{family}{series}{shape}; the size carries over from the current value, and the change commits immediately. Wrap it in a group { } to confine the effect. Passing the internal macro \f@baselineskip as \fontsize’s second argument is the standard idiom for changing type size without disturbing the leading (never rewrite internal macros, but reading one out to pass it along is fine). That said — you rarely need to write any of this. \textbf just calls \fontseries, \sffamily just calls \fontfamily; you descend to the low level only to name one specific font, or to wire a new font into a class or package.

The default macros — \rmdefault and friends

Which family \textrm or \rmfamily selects is not hard-wired; it lives in a macro. There are three — \rmdefault (roman), \sfdefault (sans), \ttdefault (typewriter) — defaulting to cmr, cmss, cmtt in the article class. To change the body font you rewrite these macros rather than any command. And as the previous section showed, these three values also decide what bold will ask for, so it is more accurate to treat them not as aliases but as the document’s font policy.

preamble.tex
% Re-point the three meta-families for the whole document
\renewcommand{\rmdefault}{ptm}  % roman   -> Times
\renewcommand{\sfdefault}{phv}  % sans    -> Helvetica
\renewcommand{\ttdefault}{pcr}  % mono    -> Courier

% The body font itself is these four (defaults: OT1, \rmdefault, m, n)
%   \encodingdefault \familydefault \seriesdefault \shapedefault
% and \familydefault points at \rmdefault, so the line above changes the body.

% Retarget bold and medium per meta-family (LaTeX2e 2020-02-02 and later)
\DeclareFontSeriesDefault[rm]{bf}{b}
\DeclareFontSeriesDefault[sf]{md}{l}

.fd files and \DeclareFontShape — where coordinates meet real files

What ties the five coordinates to actual font files is the font definition file (.fd). Its name is the encoding lowercased followed by the family — ot1cmr.fd for cmr in OT1, t1phv.fd for phv in T1 — and LaTeX loads it automatically the moment it is needed. kpsewhich ot1cmr.fd tells you where yours lives. Inside, you find one \DeclareFontFamily line and then a \DeclareFontShape for each series/shape combination.

ot1cmr.fd
% TeX Live 2024, tex/latex/base/ot1cmr.fd (excerpt)
\DeclareFontFamily{OT1}{cmr}{\hyphenchar\font45 }
\DeclareFontShape{OT1}{cmr}{m}{n}%
     {<5><6><7><8><9><10><12>gen*cmr%
      <10.95>cmr10%
      <14.4>cmr12%
      <17.28><20.74><24.88>cmr17}{}
%%%%%%% bold series
\DeclareFontShape{OT1}{cmr}{b}{n}
     {%
      <5><6><7><8><9><10><10.95><12>%
      <14.4><17.28><20.74><24.88>cmb10%
      }{}
%%%%%%%% bold extended series
\DeclareFontShape{OT1}{cmr}{bx}{n}
   {%
      <5><6><7><8><9>gen*cmbx%
      <10><10.95>cmbx10%
      <12><14.4><17.28><20.74><24.88>cmbx12%
      }{}

Reading one is just a matter of following the size ranges in angle brackets. <5><6><7><8><9>gen*cmbx means “at 5 to 9 point, use the real font whose name is the stem plus the size (cmbx5, cmbx6, …)”; <12><14.4>...cmbx12 means “from 12 point up, scale cmbx12.” The b block above shows the one-font situation described earlier: every size from 5 to 24.88 point is cmb10 stretched. There are also ssub (silent substitution) and sub (substitution with a warning); t1phv.fd, for instance, carries \DeclareFontShape{T1}{phv}{bx}{n}{<->ssub * phv/b/n}{} — Helvetica has no bx, so quietly hand out b instead.

Reading the Font shape ... undefined warning

This warning is a report that says: no such combination was in the .fd, so I picked the nearest thing on my own. It always comes as two lines — the four coordinates you asked for on the first, the four actually used plus an input line number on the second. It is not an error, so the run finishes; the PDF you get simply is not the one you asked for. In the examples above, Computer Modern has no condensed cut so c became m, and T1/cmss has no small caps so upright n was used instead.

log
% a missing shape inside a family that does exist
LaTeX Font Warning: Font shape `T1/cmss/bx/sc' undefined
(Font)              using `T1/cmss/bx/n' instead on input line 4.

% a family that does not exist at all: \DeclareFontSubstitution kicks in
LaTeX Font Warning: Font shape `T1/nosuchfont/m/n' undefined
(Font)              using `T1/cmr/m/n' instead on input line 4.

% and at the end of the run, the summary line that is easy to miss
LaTeX Font Warning: Some font shapes were not available, defaults substituted.

There are only three causes. (1) The font simply lacks that face — Helvetica has no italic, so sl stands in. You cannot conjure what is not there; choose a different font or accept the substitute. (2) The family name is misspelt — if cmr shows up on the second line, that is \DeclareFontSubstitution’s last resort firing, and it usually means a typo or a package you forgot to load. (3) You used the wrong encoding — asking in T1 for a family that only has an OT1 .fd, for example. And the single summary line at the end of the log, Some font shapes were not available, defaults substituted., is easy to skim past — but if it is there, some passage of your document is set in a face you did not choose.

Font encodings — OT1 vs T1 vs TU

A font encoding is the agreement about which slot (glyph) of the font each input character maps to. It is the first of the five attributes and the only one with no author-level command like \textbf — switching it is a package’s job, fontenc’s. OT1 is Knuth’s original 7-bit “TeX text” (the default). With room for only 128 characters, é and ñ are built by composing a base letter with an accent (\accent), and that composition is why accented words will not hyphenate and why they copy out of the PDF garbled. T1 (TeX extended text, also the Cork encoding, after the 1990 TUG meeting at Cork) is an 8-bit 256-glyph encoding that carries each accented letter as a single glyph, which makes both problems disappear at once.

Nothing shows better that an encoding is only an agreement about slot order than the pound sign in OT1. OT1 has no slot for sterling, so \textsterling in ot1enc.def reaches for the dollar-sign slot of the italic font (in Computer Modern that slot holds £). And if the current font has no slant, it switches to \fontshape{ui} first — otherwise the £ alone would come out leaning. That is why the ui declaration in ot1cmr.fd carries a warning comment saying the definition is used for LaTeX’s \pounds and must not be removed.

TU (TeX Unicode) lets you address system-installed OpenType fonts by Unicode code point directly, and is the default on XeLaTeX and LuaLaTeX. It replaced the older EU1 (for XeTeX) and EU2 (for LuaTeX): fontspec’s changelog dates the arrival of TU to v2.5, 30 January 2016, and its promotion to the default to v2.5c, 20 January 2017. In TeX Live 2024 the strings EU1 and EU2 no longer appear anywhere in fontspec.sty. Beyond these there are T2A (Cyrillic), LGR (Greek), and TS1 (the Text Companion encoding: currency, copyright and other in-text symbols); for math, OML (math italic), OMS (math symbols) and OMX (large math symbols). Note that the TS1 symbol commands moved into the kernel in 2020, so today’s textcomp package defines no symbols at all — the symbols page has that story.

EncodingWhat it coversWidth / notes
OT1Knuth’s original TeX text (the default)7-bit; accents composed, so no hyphenation
T1TeX extended text (Cork); Western languages8-bit, 256 glyphs; accents are single glyphs, hyphenation works
TUTeX Unicode; OpenType fonts addressed directlyDefault on Xe/LuaLaTeX; replaced EU1 / EU2
T2ACyrillic8-bit (T2B / T2C are siblings)
LGRGreekThe 256-glyph encoding now standard for Greek
TS1Text Companion (in-text symbols)Currency, copyright and so on; the commands moved into the kernel in 2020
OML / OMS / OMXMath italic / math symbols / large math symbolsMath only; never used for running text

inputenc vs fontenc — the way in and the way out

Two easily-confused things, separated. The input encoding governs how the bytes of your .tex source are read as characters (UTF-8 and friends) — historically the job of the inputenc package. The font encoding governs which glyph of the output font each of those characters flows into, and is fontenc’s job. Remember it as the way in (inputenc) and the way out (fontenc) and you will not mix them up. Modern pdfLaTeX reads the source as UTF-8 by default, so you almost never need to load inputenc explicitly. The font encoding, however, is still worth setting. fontenc is a package for pdfLaTeX; on XeLaTeX and LuaLaTeX you use fontspec instead, and since TU is the default there the problem barely arises.

Under pdfLaTeX, write \usepackage[T1]{fontenc}

If you typeset with pdfLaTeX, the idiom is \usepackage[T1]{fontenc} in the preamble. It switches the document’s font encoding to T1 and sweeps away the drawbacks of the OT1 default — accented words that will not hyphenate, text that garbles when copied out of the PDF. The fontenc documentation says as much: it supports widespread Western languages such as French, German, Italian and Polish, and with it LaTeX will hyphenate words with accented letters and the output can be copied and pasted. When you list several encodings as options, the last one listed becomes the default (\encodingdefault is set to it) — so for a document that mixes in Greek, write \usepackage[LGR,T1]{fontenc}, keeping the body in T1 and switching to LGR only where needed.

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}   % font encoding -> T1 (Cork)
% Input is UTF-8 by default on pdfLaTeX, so inputenc is usually unneeded.
\begin{document}
% With T1 these words hyphenate and survive copy-and-paste out of the PDF.
Na\"ive r\"esum\"e, \"uberfl\"ussig, Stra\ss{}e.
\end{document}