The tracefnt package^{∗} for use with the new font selection scheme

Frank Mittelbach  Rainer Schöpf

March 27, 1996

1 Introduction

This package contains the code for tracing font loading and font changes. It basically overlays some of the low-level functions of NFSS with additional code used for tracing.

The package accepts the following options:

errorshow Write all information about font changes etc. only to the transcript file unless an error happens. This means that information about font substitution will not be shown on the terminal.

warningshow Show all NFSS warnings on the terminal. This setting corresponds to the default behaviour of NFSS if the tracefnt package is not loaded!

infoshow Show all NFSS warning and all NFSS info messages (that are normally only written to the transcript file) also on the terminal. This is the default if the tracefnt package is loaded.

debugshow In addition to infoshow show also changing of math fonts as far as possible (this option can produce a large amount of output.

loading Show the name of external fonts when they are loaded. This option shows only “newly” loaded fonts not those already preloaded in the format or the class file before the tracefnt package became active.

pausing Turn all font warnings into errors so that L^{A}TEX will stop.

2 A driver for this document

The next bit of code contains the documentation driver file for TEX, i.e., the file that will produce the documentation you are currently reading. It will be extracted from this file by the DocStrip program.

∗This file has version number v3.0g, dated 1995/11/28.

When this file is processed directly by L^{A}TEX this will produce the documentation as well.

1 ⟨∗driver⟩

 2 \documentclass{ltxdoc}

3 4

 5 %\OnlyDescription  % comment out for implementation details

6

 7 \begin{document}
 8 \DocInput{ltfsstrc.dtx}
 9 \end{document}

10 ⟨/driver⟩

3 The Implementation

Warning: Read the macro documentation with a grain of salt. It is still basically the documentation from the first NFSS release and therefore in some cases probably not completely accurate.

If we are making a package file it is a good idea to test whether we are running under 2e. This code is actually placed at the very beginning of this file for easier maintenance, thus commented out here.

11 ⟨∗package⟩

12 %\NeedsTeXFormat{LaTeX2e}
13 %\ProvidesPackage{tracefnt}[??/??/?? v?.??
14 %                           Standard LaTeX package (font tracing)]

15 ⟨/package⟩

The debug module makes use of commands contained in a special package file

named trace.sty.^{1}
16 ⟨+debug⟩ \input trace.sty

4 Handling Options

\tracingfonts Here is the definition of the integer register for the font trace. As a default in a package file we use 1 to give error messages if fonts are substituted. If this code is used for debugging or tracing reasons in the format file (i.e. in fam.dtx) we use 0 as the default. But if no font trace is used we build a definition that will produce a warning message.

17 ⟨∗2ekernel | autoload⟩

18 \def\tracingfonts{%
19 \@font@warning{Command \noexpand\tracingfonts
20  not provided.\MessageBreak
21  Use the `tracefnt' package.\MessageBreak Command found:}%

1This package is not in distribution at the moment (and probably doesn’t any longer work).

Think of this part of the code as being historical artefacts.

22  \count@}

23 ⟨/2ekernel | autoload⟩

The \count@ in the line above will remove the number after \tracingfonts. Note that this definition will be overwritten be the next line if one of these modules are included.

24 ⟨∗package, trace, debug⟩

25 \newcount\tracingfonts
26 \tracingfonts=0

27 ⟨/package, trace, debug⟩

The option errorshow turns off all warnings so that only real errors are shown. warningshow corresponds to the NFSS default (when tracefnt is not loaded). infoshow is the default for this package here; and debugshow, loading, and pausing extend the amount of information even further.

28 ⟨∗package⟩

29 \DeclareOption{errorshow}{%
30 \def\@font@info#1{%
31  \GenericInfo{(Font)\@spaces\@spaces\@spaces\space\space}%
32  {LaTeX Font Info: \space\space\space#1}}%
33  \def\@font@warning#1{%
34  \GenericInfo{(Font)\@spaces\@spaces\@spaces\space\space}%
35  {LaTeX Font Warning: #1}}%

36 }

37 \DeclareOption{warningshow}{%
38 \def\@font@info#1{%
39  \GenericInfo{(Font)\@spaces\@spaces\@spaces\space\space}%
40  {LaTeX Font Info: \space\space\space#1}}%
41  \def\@font@warning#1{%
42  \GenericWarning{(Font)\@spaces\@spaces\@spaces\space\space}%
43  {LaTeX Font Warning: #1}}%

44 }

45 \DeclareOption{infoshow}{%
46 \def\@font@info#1{%
47  \GenericWarning{(Font)\@spaces\@spaces\@spaces\space\space}%
48  {LaTeX Font Info: \space\space\space#1}}%
49  \def\@font@warning#1{%
50  \GenericWarning{(Font)\@spaces\@spaces\@spaces\space\space}%
51  {LaTeX Font Warning: #1}}%

52 }

53 \DeclareOption{loading}{%
54  \tracingfonts\tw@

55 }

56 \DeclareOption{debugshow}{%
57  \ExecuteOptions{infoshow}%
58  \tracingfonts\thr@@

59 }

60 \DeclareOption{pausing}{%
61  \def\@font@warning#1{%
62  \GenericError
63  {(Font)\@spaces\@spaces\@spaces\space\space}%
64  {LaTeX Font Warning: #1}%
65  {See the LaTeX Companion for details.}%
66  {I'll stop for every LaTeX Font Warning because
67  you requested\\the `pausing' option
68  to the tracefnt package.}}%

69 }

We make infoshow the default, which in turn defines \font@warning and

\font@info.
70 \ExecuteOptions{infoshow}
71 \ProcessOptions

72 ⟨/package⟩

We also need a default definition inside the kernel:

73 ⟨∗2ekernel | autoload⟩

74 \def\@font@info#1{%
75  \GenericInfo{(Font)\@spaces\@spaces\@spaces\space\space}%
76  {LaTeX Font Info: \space\space\space#1}}%
77 \def\@font@warning#1{%
78  \GenericWarning{(Font)\@spaces\@spaces\@spaces\space\space}%
79  {LaTeX Font Warning: #1}}%

80 ⟨/2ekernel | autoload⟩

5 Macros common to fam.tex and tracefnt.sty

In the first versions of tracefnt.dtx some macros of fam.dtx^{2} were redefined to included the extra tracing information. Now these macros are all defined in this file (i.e. removed from fam.dtx) and different production versions can be obtained simply by specifying a different set of modules to include when generating

ltfss.dtx.

5.1 General font loading

\extract@font This macro organizes the font loading. It first calls \get@external@font which will return in \external@font the name of the external font file (the .tfm) as it was determined by the NFSS tables.

81 ⟨∗2ekernel | package | autoload⟩

82 \def\extract@font{%
83 \get@external@font

Then the external font is loaded and assigned to the font identifier stored inside

\font@name (for this reason we need \expandafter).
84 \global\expandafter\font\font@name\external@font\relax

2This file is currently not distributed in documented form. Its code is part of ltfss.dtx.

When tracing we typeout the internal and external font name.

85 ⟨∗trace⟩

86  \ifnum \tracingfonts >\@ne
87  \@font@info{External font `\external@font'
88  loaded as\MessageBreak \font@name}\fi

89 ⟨/trace⟩

Finally we call the corresponding “loading action” macros to finish things. First the font is locally selected to allow the use of \font inside the loading action macros.

90  \font@name \relax

The next two lines execute the “loading actions” for the family and then for the individual font shape.

91  \csname \f@encoding+\f@family\endcsname
92  \csname\curr@fontshape\endcsname
93  \relax

94 } 95 ⟨/2ekernel | package | autoload⟩

The \relax at the end needs to be explained. This is inserted to prevent TEX from scanning too far when it is executing the replacement text of the loading code macros.

\get@external@font This function tries to find an external font name. It will place the name into the macro \external@font. If no font is found it will return the one that was defined

via \DeclareErrorFont.

96 ⟨∗2ekernel | autoload⟩

97 \def\get@external@font{%

We don’t know the external font name at the beginning.

98 \let\external@font\@empty
99 \edef\font@info{\expandafter\expandafter\expandafter\string
100  \csname \curr@fontshape \endcsname}%
101 \try@size@range

If this failed, we’ll try to substitute another size of the same font. This is done

by the \try@size@substitution macro. It “knows about” \do@extract@font,
\font@name, \f@size, and so on.
102 \ifx\external@font\@empty
103  \try@size@substitution
104  \ifx\external@font\@empty
105  \@latex@error{Font \expandafter \string\font@name\space
106  not found}\@eha
107  \error@fontshape
108  \get@external@font
109 \fi\fi

110 } 111 ⟨/2ekernel | autoload⟩

\selectfont The macro \selectfont is called whenever a font change must take place.

112 ⟨∗2ekernel | package | autoload⟩

113 \DeclareRobustCommand\selectfont

114 {%

When debug is specified we actually want something like ‘undebug’. The font selection is now stable so that using \tracingall on some other macros will show us a lot of unwanted information about font loading. Therefore we disable tracing during font loading as long as \tracingfonts is less than 4.

115 ⟨+debug⟩ \pushtracing
116 ⟨+debug⟩ \ifnum\tracingfonts<4 \tracingoff
117 ⟨+debug⟩ \else \tracingon\p@selectfont \fi

If \baselinestretch was redefined by the user it will not longer match its internal counterpart \f@linespread. If so we call \set@fontsize to prepare

\size@update.
118  \ifx\f@linespread\baselinestretch \else
119  \set@fontsize\baselinestretch\f@size\f@baselineskip \fi

Then we generate the internal name of the font by concatenating family, series, shape, and current size, with slashes as delimiters between them. This is much more readable than standard L^{A}TEX’s \twfbf, etc. We define \font@name globally, as always. The reason for this is explained later on.

120  \xdef\font@name{%
121  \csname\curr@fontshape/\f@size\endcsname}%

We call the macro \pickup@font which will load the font if necessary.

122  \pickup@font

Then we select the font.

123  \font@name

If \tracingfonts is greater than 2 we also show the font switch. We do this before \glb@settings is called since this macro might redefine \font@name.

124 ⟨∗trace⟩

125  \ifnum \tracingfonts>\tw@
126  \@font@info{Switching to \font@name}\fi

127 ⟨/trace⟩

Finally we call \size@update. This macro is normally empty but will contain actions (like setting the \baselineskip) that have to be carried out when the font size, the base \baselineskip or the \baselinestretch have changed.

128  \size@update

A similar function is called to handle anything related to encoding updates. This

one is changed from \relax by \fontencoding.
129  \enc@update

Just before ending this macro we have to pop the tracing stack if it was pushed before.

130 ⟨+debug⟩ \poptracing

131 }

\set@fontsize The macro \set@fontsize does the actual work. First it assigns new values to

\f@size, \f@baselineskip and \f@linespread.
132 \def\set@fontsize#1#2#3{%
133  \@defaultunits\@tempdimb#2pt\relax\@nnil
134  \edef\f@size{\strip@pt\@tempdimb}%
135  \@defaultunits\@tempskipa#3pt\relax\@nnil
136  \edef\f@baselineskip{\the\@tempskipa}%
137  \edef\f@linespread{#1}%

For backward compatibility and for later testing within \selectfont the internal

value of \f@linespread is passed back to \baselinestretch.
138  \let\baselinestretch\f@linespread

Additional processing will happen within \selectfont. For this reason the macro \size@update (which will be called in \selectfont) will be defined to be:

139  \def\size@update{%

First calculate the new \baselineskip and also store it in normalbaselineskip

140  \baselineskip\f@baselineskip\relax
141  \baselineskip\f@linespread\baselineskip
142  \normalbaselineskip\baselineskip

then to set up a new \strutbox

143  \setbox\strutbox\hbox{%
144  \vrule\@height.7\baselineskip
145  \@depth.3\baselineskip
146  \@width\z@}%

We end with a bit of tracing information.

147 ⟨∗trace⟩

148 \ifnum \tracingfonts>\tw@
149  \ifx\f@linespread\@empty
150  \let\reserved@a\@empty
151  \else
152  \def\reserved@a{\f@linespread x}%
153  \fi
154  \@font@info{Changing size to \f@size/\reserved@a
155  \f@baselineskip}%
156  \aftergroup\type@restoreinfo \fi

157 ⟨/trace⟩

When all this is processed \size@update redefines itself to \relax so that in later calls of \selectfont no extra code will be executed.

158  \let\size@update\relax}%

159 }

Instead of defining this macro internally we might speed things up by placing the code into a separate macro and use \let!

\size@update Normally this macro does nothing; it will be redefined by \set@fontsize to initiate an update.

160 \let\size@update\relax

\type@restoreinfo This macro produces some info when a font size and/or baseline change will get restored.

161 ⟨∗trace⟩

162 \def\type@restoreinfo{%
163  \ifx\f@linespread\@empty
164  \let\reserved@a\@empty
165  \else
166  \def\reserved@a{\f@linespread x}%
167  \fi
168  \@font@info{Restoring size to
169  \f@size/\reserved@a\f@baselineskip}}

170 ⟨/trace⟩

\glb@settings \glb@currsize The macro \glb@settings globally selects all math fonts for the current size if necessary.

171 \def\glb@settings{%

When \glb@settings gains control a size change was requested and all previous font assignments need to be replaced. Therefore the old values of the fonts are no longer needed. For every math group the new assignments are appended to \math@fonts. But this happens only if the math@fonts switch is set to true. However, we always set up the correct math sizes for script and scriptscript fonts since they may be needed even if we don’t set up the whole math machinery.

Here we set the math size, script size and scriptscript size. If the S@… macro is not defined we have to first calculate the three sizes.

172  \expandafter\ifx\csname S@\f@size\endcsname\relax
173  \calculate@math@sizes
174  \fi

The effect of this is that \calculate@math@sizes may or may not define the S@… macro. In the first case the next time the same size is requested this macro is used, otherwise \calculate@math@sizes is called again. This also sets the math@fonts switch. If it is true we must switch the math fonts.

175  \csname S@\f@size\endcsname
176  \ifmath@fonts

177 ⟨∗trace⟩

178  \ifnum \tracingfonts>\tw@
179  \@font@info{Setting up math fonts for
180  \f@size/\f@baselineskip}\fi

181 ⟨/trace⟩

Inside a group we execute the macro for the current math version. This sets

\math@fonts to a list of \textfont. . . assignments. \getanddefine@fonts

(which may be called at this point) needs the \escapechar parameter to be set to −1.

182  \begingroup
183  \escapechar\m@ne
184  \csname mv@\math@version \endcsname

Then we set \globaldefs to 1 so that all following changes are done globally. The math font assignments recorded in \math@fonts are executed and \glb@currsize is set equal to \f@size. This signals that the fonts for math in this size are set up.

185  \globaldefs\@ne
186  \math@fonts
187  \let \glb@currsize \f@size
188  \endgroup

Finally we execute any code that is supposed to happen whenever the math font setup changes. This register will be executed in local mode which means that everything that is supposed to have any effect should be done globally inside. We can’t execute it within \globaldefs\@ne as we don’t know what ends up inside this register, e.g., it might contain calculations which use some local registers to calculate the final (global) value.

189  \the\every@math@size

Otherwise we announce that the math fonts are not set up for this size.

190 ⟨∗trace⟩

191  \else
192  \ifnum \tracingfonts>\tw@
193  \@font@info{No math setup for
194  \f@size/\f@baselineskip}\fi

195 ⟨/trace⟩

196  \fi

197 } 198 ⟨/2ekernel | package | autoload⟩

\baselinestretch In \selectfont we used \baselinestretch as a factor when assigning a value to

\baselineskip. We use 1 as a default (i.e. no stretch).

199 ⟨∗2ekernel | autoload⟩

200 \def\baselinestretch{1}
\every@math@size We must still define the hook \every@math@size we used in \glb@settings. We

initialize it to nothing. It is important to remember that everything that goes into this hook should to global updates, local changes will have weird effects.

201 \newtoks\every@math@size
202 \every@math@size={}

203 ⟨/2ekernel | autoload⟩

5.2 Math fonts setup

5.2.1 Outline of algorithm for math font sizes

TEX uses the the math fonts that are current when the end of a formula is reached. If we don’t want to keep font setups local to every formula (which would result in an enormous overhead, we have to be careful not to end up with the wrong setup in case formulas are nested, e.g., we need to be able to handle

 $ a=b+c \mbox{ \small for all $b$ and $c\in Z$}$

Here the inner formulae b and c\in Z are typeset in \small but we have to return to \normalsize before we reach the closing $ of the outer formula.

This is handled in the following way:

1. At any point in the document the global variable \gbl@currsize contains the point size for which the math fonts currently are set up.

2. Whenever we start a formula we compare its value with the local variable \f@size that describes the current text font size.

3. If both are the same we assume that we can use the current math font setup without adjustment.

4. If they differ we call \gbl@settings which changes the math font setup and

updates \gbl@currsize.

(a) If we are recursively inside another formula (\if@inmath) we ensure that \gbl@settings is executed again in the outer formula, so that the old setup is automatically restored.

(b) Otherwise, we set the switch @inmath locally to true so that all nested formulae will be able to detect that they are nested in some outer formula.

The above algorithm has the following features:

• For sizes which are not containing any formula no math setup is done. Compared to the original algorithm of NFSS this results in the following savings:

– No unnecessary loading of math fonts for sizes that are not used to typeset any math formulae (explicit or implicit ones).

– No time overhead due to unnecessary changes of the math font setup on entrance and exit of the text font size.

• Math font setup changes for top-level formulae will survive (there is no restoration after the formula) thus any following formula in the same size will be directly typesetable. Compared to original implementation in NFSS2 the new algorithm has the overhead of one test per formula to see if the current math setup is valid (in the original algorithm the setup was always valid, thus no test was necessary).

• In nested formulae the math font setup is restored in the outer formula by a series of \aftergroup commands and checks. Compared to the original algorithm this involves additional checks (2 × ⟨non-math levels⟩ per inner formula).

5.2.2 Code for math font size setting

\check@mathfonts In the \check@mathfonts macros we implement the steps 2 to 4.

204 ⟨∗2ekernel | package | autoload⟩

205 \def\check@mathfonts{%
206 \ifx \glb@currsize \f@size

207 ⟨∗trace⟩

208  \ifnum \tracingfonts>\thr@@
209  \@font@info{*** MATH: no change \f@size\space
210  curr/global (\curr@math@size/\glb@currsize)}\fi

211 ⟨/trace⟩

212 \else

213 ⟨∗trace⟩

214  \ifnum \tracingfonts>\thr@@
215  \@font@info{*** MATH: setting up \f@size\space
216  curr/global (\curr@math@size/\glb@currsize)}\fi

217 ⟨/trace⟩

218  \glb@settings
219  \if@inmath
220  \aftergroup\restglb@settings

221 ⟨∗trace⟩

222  \else
223  \ifnum \tracingfonts>\thr@@
224  \@font@info{*** MATH: no resetting (not in
225  nested math)}\fi

226 ⟨/trace⟩

227  \fi
228 \fi
229 \let\curr@math@size\f@size
230 \@inmathtrue

231 }

\restglb@settings This macro will be executed the first time after the current formula.

232 \def\restglb@settings{%

233 ⟨∗trace⟩

234  \ifnum \tracingfonts>\thr@@
235  \@font@info{*** MATH: restoring}\fi

236 ⟨/trace⟩

237  \begingroup
238  \let\f@size\curr@math@size
239  \ifx\glb@currsize \f@size

240 ⟨∗trace⟩

241  \ifnum \tracingfonts>\thr@@
242  \@font@info{*** MATH: ... already okay (\f@size)}\fi

243 ⟨/trace⟩

244  \else

245 ⟨∗trace⟩

246  \ifnum \tracingfonts>\thr@@
247  \@font@info{*** MATH: ... to \f@size}\fi

248 ⟨/trace⟩

249  \glb@settings
250  \fi
251  \endgroup

252 }

5.2.3 Other code for math

\use@mathgroup The \use@mathgroup macro should be used in user macros to select a math group. Depending on whether or not the margid option is in force it has two or three arguments. For this reason it should be called as the last macro.

First we test if we are inside math mode since we don’t want to apply a useless definition.

253 \def\use@mathgroup#1#2{\relax\ifmmode

254 ⟨∗trace⟩

255 \ifnum \tracingfonts>\tw@
256  \count@#2\relax
257  \@font@info{Using \noexpand\mathgroup
258  (\the\count@) #2}\fi

259 ⟨/trace⟩

If so we first call the ‘=’ macro (i.e. argument three) to set up special things for the selected math group. Then we call \mathgroup to select the group given by argument two and finally we place #1 (i.e. the argument of the ⟨math alphabet identifier⟩ at the end. This part of the code is surrounded by two commands which behave like \begingroup and \endgroup if we want ⟨math alphabet identifier⟩s but will expand into \@empty if we want simply switches to a new math group. Since argument number 2 may be a digit instead of a control sequence we add a \relax. Otherwise something like \mit{1} would switch to math group 11 (and back) instead of printing an oldstyle 1.

260  \math@bgroup
261  \expandafter\ifx\csname M@\f@encoding\endcsname#1\else
262  #1\fi
263  \mathgroup#2\relax

Before we reinsert the swallowed token (arg. three) into the input stream, in the case that the ⟨math alphabet identifier⟩ isn’t called in math mode, we remove the \fi with the \expandafter trick. This is necessary if the token is actually an macro with arguments. In such a case the \fi will be misinterpreted as the first argument which would be disastrous.

264  \expandafter\math@egroup\fi}%

The surrounding macros equal \begingroup and \endgroup. But using internal names makes it possible to overwrite their meaning in certain cases. This is for example used in AMS-TEX macros for placing accents.

\math@egroup If the margid option is in force (which can be tested by looking at the definition of \math@bgroup we change the \math@egroup command a bit to display the current ⟨math group number⟩ after it closes the scope of ⟨math alphabet⟩ with \endgroup.

265 ⟨∗trace⟩

266 \ifx\math@bgroup\begingroup
267  \def\math@egroup#1{#1\endgroup
268  \ifnum \tracingfonts>\tw@
269  \@font@info{Restoring \noexpand\mathgroup
270  (\ifnum\mathgroup=\m@ne default\else \the\mathgroup \fi)%
271  }\fi}
272 \fi

273 ⟨/trace⟩

\getanddefine@fonts \getanddefine@fonts has two arguments: the ⟨math group number⟩ and the family/series/shape name as a control sequence.

274 \def\getanddefine@fonts#1#2{%

First we turn of tracing when \tracingfonts is less than 4.

275 ⟨+debug⟩ \pushtracing
276 ⟨+debug⟩ \ifnum\tracingfonts<4 \tracingoff
277 ⟨+debug⟩ \else \tracingon\getanddefine@fonts \fi

278 ⟨∗trace⟩

279 \ifnum \tracingfonts>\tw@
280 \count@#1\relax
281  \@font@info{\noexpand\mathgroup (\the\count@) #1 :=\MessageBreak
282  \string#2 \tf@size/\sf@size/\ssf@size}\fi

283 ⟨/trace⟩

We append the current \tf@size to #2 to obtain the font name.^{3} Again, font@name is defined globally, for the reasons explained in the description of

 \wrong@fontshape.
284 \xdef\font@name{\csname \string#2/\tf@size\endcsname}%

Then we call \pickup@font to load it if necessary. We remember the internal

 name as \textfont@name.
285 \pickup@font \let\textfont@name\font@name
 Same game for \scriptfont and \scriptscriptfont:
286 \xdef\font@name{\csname \string#2/\sf@size\endcsname}%
287 \pickup@font \let\scriptfont@name\font@name
288 \xdef\font@name{\csname \string#2/\ssf@size\endcsname}%
289 \pickup@font

Then we append the new \textfont… assignments to the \math@fonts.

290 \edef\math@fonts{\math@fonts
291  \textfont#1\textfont@name
292  \scriptfont#1\scriptfont@name
293  \scriptscriptfont#1\font@name}%

3One might ask why this expansion does not generate a macro name that starts with an additional \ character. The solution is that \escapechar is set to −1 before \getanddefine@fonts is called.

Just before ending this macro we have to pop the tracing stack if it was pushed before.

294 ⟨+debug⟩ \poptracing

295 } 296 ⟨/2ekernel | package | autoload⟩

6 Scaled font extraction

\ifnot@nil We begin with a simple auxiliary macro. It checks whether its argument is the token \@nil. If so, it expands to \@gobble which discards the following argument, otherwise it expands to \@firstofone which reproduces it argument.

297 ⟨∗2ekernel | autoload⟩

298 \def\ifnot@nil#1{\def\reserved@a{#1}%
299 \ifx\reserved@a\@nnil \expandafter\@gobble
300 \else \expandafter\@firstofone\fi}
\remove@to@nnil \remove@angles \remove@star Three other auxiliary macros will be needed in the following: \remove@to@nnil

gobbles up everything up to, and including, the next \@nnil token, and \remove@angles and \remove@star do the same for the character > and *, respectively, instead of \@nnil.

301 \def\remove@to@nnil#1\@nnil{}
302 \def\remove@angles#1>{\set@simple@size@args}
303 \def\remove@star#1*{#1}

304 ⟨/2ekernel | autoload⟩

\extract@sizefn This macro takes a size specification and parses it into size function and the optional and mandatory arguments.

305 ⟨∗2ekernel | def2 | autoload⟩

306 \def\extract@sizefn#1*#2\@nil{%
307 \if>#2>\set@size@funct@args#1\@nil
308  \let\sizefn@info\@empty
309 \else\expandafter\set@size@funct@args\remove@star#2\@nil
310  \def\sizefn@info{#1}\fi

311 }

\try@simple@size This function tries to extract the given size (specified by \f@size) for the requested font shape. The font information must already be present in \font@info. The central macro that does the real work is \extract@fontinfo. We will first give a simple example how this macro works, and describe it in full generality later.

Assume that the requested parameters are: encoding scheme ‘OT1’, family ‘cm’, series ‘sansserif’, shape ‘normal’, and size ‘12’. The correspondign font definitions have already been extracted from the macro \OT1/cm/sansserif/normal and stored in font@info. (Otherwise \extract@fontinfo doesn’t get called.) This information consists of a token list made of characters of category code 12 of the form

       <10*>cmss10<12*>cmss12<17*>cmss17

For reasonable packages one usually needs more sizes but this is sufficient to get the flavour. We will define a macro \extract@fontinfo to find the external font name (‘cmss12’) for us:

      \def\extract@fontinfo#1<12*#2>#3<#4\@nnil{%
          \set@simple@size@args#3<#4\@nnil
          \execute@size@function{#2}}

so that when it gets called via

      \extract@fontinfo<10*>cmss10<12*>cmss12<17*>cmss17\@nnil

#1 will contain all characters before <12*>, #2 will be empty, #3 will be exactly cmss12, and #3 will be 17>cmss17. The expansion is therefore

      \set@simple@size@args cmss12<17*>cmss17\@nnil
      \execute@size@function{}

This means: the default (empty) size function will be executed, with its optional argument argument set to empty and its mandatory argument set to cmss12 by \set@simple@size@args. As we discussed earlier, the effect of the default size function is to load the given external font (cmss12) at the specified size (12)— which is exactly what was intended.

But this is only part of the whole story. It may be that the size requested does not occur in the token list \font@info. And the simple definition of \extract@fontinfo we gave above does not allow to specify give more than one size specification in front of the external font name.

Let’s address these two problems separately. The first one is solved with the following trick: We define \extract@fontinfo as follows:

      \def\extract@fontinfo#1<12*#2>#3<#4\@nnil{%
        \ifnot@nil{#3}%
          {\set@simple@size@args#3<#4\@nnil
           \execute@size@function{#2}%
          }}%

How does this work? We call \extract@fontinfo via

      \expandafter\extract@fontinfo\font@info<12*>\@nil<\@nnil

i.e. by appending <12*>\@nil<\@nnil. If the size (‘12’ in this case) appears in \font@info everything works as explained above, the only difference being that argument #4 of \extract@fontinfo additionally gets the tokens <12*>\@nil<\@nnil. However, if the size is not found everything up to the final <12*> is in argument #1, #3 gets \@nil, and #2 and #4 are empty. The macro \ifnot@nil will discard the calls to \set@simple@size@args and execute@size@function, and hence \font@info will continue to be equal to \@empty. This means that no simple size specification matching the requested size could be found.

The second problem (more than one simple size specification for one external font name) will be addressed in \set@simple@size@args below.

The macros are hidden inside other control sequences so that we have to build

\extract@fontinfo in several steps.

So here’s the actual definition of \extract@font in \try@simple@size.

312 % % this could be replaced by \try@size@range making the subst slower!
313 \def\try@simple@size{%

\reserved@a is made an abbreviation for the head of the definition of the macro

\extract@fontinfo.
314  \def\reserved@a{\def\extract@fontinfo####1}%

Now we can define \extract@fontinfo. Here we handle a small but convenient variation: in case of the default (empty) size function it is allowed to omit the * character.

315  \expandafter\reserved@a\expandafter<\f@size>##2<##3\@nnil{%
316  \ifnot@nil{##2}%
317  {\set@simple@size@args##2<##3\@nnil
318  \execute@size@function\sizefn@info
319  }}%

Now we call \extract@fontinfo. Note the <\@nil tokens at the end.

320  \expandafter\expandafter
321  \expandafter\extract@fontinfo\expandafter\font@info
322  \expandafter<\f@size>\@nil<\@nnil

323 }

\set@simple@size@args As promised above, the macro \set@simple@size@args will handle the case of several size specifications in a row. If another size specification follows, the very first token of its argument list is the character <. By starting the definition as follows,

324 \def\set@simple@size@args#1<{%

parameter #1 is empty in this case, and contains the size function’s arguments otherwise. We distinguish these two cases (Note that the character < cannot

appear in #1) by calling \remove@angles for empty #1 and \extract@sizefn

otherwise. In the latter case we have to take care of the remaining character tokens and discard them. This is done by \remove@to@nnil. Note also the use of Kabelschacht’s method.

325  \if<#1<%
326  \expandafter\remove@angles
327  \else
328  \extract@sizefn#1*\@nil
329  \expandafter\remove@to@nnil
330  \fi}

Now, we are through with the case of a simple size, except for calling the size function. This will be handled later, as it is the same mechanism for all types of size specification. We will now proceed to macors for extraction of size range specification.

\extract@rangefontinfo \extract@rangefontinfo goes through a font shape definition in the input until it recognizes the tokens <\@nil->. It looks for font ranges with font size functions. It’s operation is rather simple: it discards everything up to the next size specification and passes this on to \is@range for inspection. The specification (parameter #2 is inserted again, in case it is needed later.

331 \def\extract@rangefontinfo#1<#2>{%
332  \is@range#2->\@nil#2>}

\is@range \is@range is again a sort of dispatcher macro: if the size specification it is looking at is not a range specification it discards it and calls \extract@rangefontinfo to continue the search. Otherwise it calls \check@range to check the requested size against the specified range.

   From the way \is@range is called inside \extract@rangefontinfo we see

that #2 is the character > if the size specification found is a simple one (as it does not contain a - character. This is checked easily enough and \extract@rangefontinfo called again. Note that the extra tokens inserted after the \@nil in the call to \is@range appear at the beginning of the first argument

to \extract@rangefontinfo and are hence ignored.
333 \def\is@range#1-#2\@nil{%
334 \if>#2\expandafter\check@single\else
335  \expandafter\check@range\fi}

\check@range \check@range takes lower bound as parameter #1, upper bound as #2, size function as #3 and the size function’s arguments as #4. If #3 is the special token \@nil \font@info is exhausted and we can stop searching.

336 \def\check@range#1-#2>#3<#4\@nnil{%
337 \ifnot@nil{#3}{%

If #3 wasn’t \@nil we have a range. We start by assuming that we have to recurse. Note that we have to reinsert an < as it was already removed by scanning.

338  \def\reserved@f{\extract@rangefontinfo<#4\@nnil}%

We have to make sure that both boundaries are present, if not we have to set them. Here we check the upper bound. If \upper@bound is zero after the assignment we set it to \maxdimen (upper open range). We need to use a ⟨dimen⟩ register for the scan since we may have a decimal number as the boundary.

339  \upper@bound0#2\p@
340  \ifdim\upper@bound=\z@ \upper@bound\maxdimen\fi

Now we check the upper boundary against \f@size. If it is larger or equal than \f@size this range is no good and we have to recurse.

341  \ifdim \f@size \p@<\upper@bound

Otherwise we have to check the lower bound. This time it is not necessary to scan the boundary value into a register because if it is empty we get zero as desired. We could even omit the 0 which would result in 1pt as default lower boundary. If \f@size is smaller than the boundary we have to recurse.

342  \lower@bound0#1\p@
343  \ifdim \f@size \p@<\lower@bound
344  \else

If both tests are passed we can try executing the size function.

345  \set@simple@size@args#3<#4\@nnil
346  \execute@size@function\sizefn@info

If the function was successful it should have left an external font name in \external@font. We use this to see if we can stop scanning. Otherwise we recurse.

347  \ifx\external@font\@empty
348  \else
349  \let\reserved@f\@empty
350  \fi
351  \fi
352  \fi
353  \reserved@f}}

354 ⟨/2ekernel | def2 | autoload⟩

\lower@bound \upper@bound We use two dimen registers \lower@bound and \upper@bound to store the lower

and upper endpoints of the range we found.

355 ⟨∗2ekernel | autoload⟩

356 \newdimen\lower@bound
357 \newdimen\upper@bound

358 ⟨/2ekernel | autoload⟩

\check@single \check@single takes the size as parameter #1, size function as #2 and the size function’s arguments as #3. We can assume that there is always something in the pipeline since the very last entry is a faked range (see above).

359 ⟨∗2ekernel | def2 | autoload⟩

360 \def\check@single#1>#2<#3\@nnil{%

We start by assuming that we have to recurse. Note that we have to reinsert an < as it was already removed by scanning.

361  \def\reserved@f{\extract@rangefontinfo<#3\@nnil}%

Now we check the the size against \f@size. If it is not equal \f@size it is no good and we have to recurse.

362  \ifdim \f@size \p@=#1\p@

Otherwise if this test is passed we can try executing the size function.

363  \set@simple@size@args#2<#3\@nnil
364  \execute@size@function\sizefn@info

If the function was successful it should have left an external font name in \external@font. We use this to see if we can stop scanning. Otherwise we recurse.

365  \ifx\external@font\@empty
366  \else
367  \let\reserved@f\@empty
368  \fi
369  \fi
370  \reserved@f}

\set@size@funct@args \set@size@funct@args@ This macro sets the optional and mandatory arguments for a size function. If the optional argument is not present it is set to the empty token list. The mandatory argument is delimited by the token \@nil.

371 \def\set@size@funct@args{\@ifnextchar[%
372 \set@size@funct@args@{\set@size@funct@args@[]}}
373 \def\set@size@funct@args@[#1]#2\@nil{%
374 \def\mandatory@arg{#2}%
375 \def\optional@arg{#1}}

376 ⟨/2ekernel | def2 | autoload⟩

\DeclareSizeFunction This function defines a new size function hiding the internal from the designer. The body of the size function may use \optional@arg and \mandatory@arg denoting the optional and mandatory argument that may follow the size specification <...>.

377 ⟨∗2ekernel | autoload⟩

378 \def\DeclareSizeFunction#1#2{\@namedef{s@fct@#1}{#2}}
379 \@onlypreamble\DeclareSizeFunction

380 ⟨/2ekernel | autoload⟩

\execute@size@function This macro is very simple. The only point worth noting is that calling an undefined size function will do nothing (actually execute a \relax).

381 ⟨∗2ekernel | package | autoload⟩

382 \def\execute@size@function#1{%    %%% could be added to autoload as well

383 ⟨∗trace⟩

384  \@ifundefined{s@fct@#1}%
385  {\errmessage{Undefined font size function #1}%
386  \s@fct@}%
387  {\csname s@fct@#1\endcsname}%

388 ⟨/trace⟩

389 ⟨−trace⟩  \csname s@fct@#1\endcsname

390 } 391 ⟨/2ekernel | package | autoload⟩

\try@size@range This macro tries to find a suitable range for requested size (specified by \f@size)

in \font@info. All the relevant action is done in \extract@rangefontinfo.

All that needs to be done is to stuff in the token list in \font@info so that \extract@rangefontinfo can inspect it. Note the <-*\@nil>< token at the end to stop scanning.

392 ⟨∗2ekernel | def2 | autoload⟩

393 \def\try@size@range{%
394  \expandafter\extract@rangefontinfo\font@info <-*>\@nil<\@nnil

395 } 396 ⟨/2ekernel | def2 | autoload⟩

\try@size@substitution This is the last thing that can be tried. If the desired \f@size is found neither among the simple size specifications nor in one of the ranges the whole list of size specifications is searched for a nearby simple size.

397 ⟨∗2ekernel | def1⟩

398 \gdef\try@size@substitution{%

First we do some initializations. \@tempdimb will hold the difference between the wanted size and the best solution found so far, so we initialise it with \maxdimen. The macro \best@size will hold the best size found, nothing found is indicated by the empty value.

399 \@tempdimb \maxdimen
400 \let \best@size \@empty

Now we loop over the specification

401 \expandafter \try@simples \font@info <\number\@M>\@nil<\@nnil

402 } 403 ⟨/2ekernel | def1⟩ 404 ⟨∗autoload⟩

405 \def\try@size@substitution{\try@simples\try@size@substitution}

406 ⟨/autoload⟩

\font@submax \fontsubfuzz The macro \font@submax records the maximal deviation from the desired size encountered so far. Its value is used in a warning message at \end{coument}. The macro \fontsubfuzz contains the amount that will not cause terminal warnings (warnings still go into the transcript file).

407 ⟨∗2ekernel | autoload⟩

408 \def\font@submax{0pt}
409 \def\fontsubfuzz{.4pt}

410 ⟨/2ekernel | autoload⟩

411 ⟨+package⟩\def\fontsubfuzz{0pt}

\try@simples \try@simples goes through a font shape definition in the input until it recognizes the tokens <*\@nil><. It looks for simple sizes to determine the two closest sizes. It is assumed that simple sizes are in increasing order.

412 ⟨∗2ekernel | def1⟩

413 \gdef\try@simples#1<#2>{%
414 \tryif@simple#2->\tryif@simple}

415 ⟨/2ekernel | def1⟩ 416 ⟨∗autoload⟩

417 \def\try@simples{\@autoload{fss1}}

418 ⟨/autoload⟩

\tryis@simple \tryis@simple is similar to \is@range. If it sees a simple size, it checks it against

the value of \f@size and sets \lower@font@size or \higher@font@size. In the

latter case, it stops the iteration. By adding <\number\@M> at the end of the line we always have an end point. This is a hack which probably should be corrected.

First it checks whether it is finished already, then whether the size specification in question is a simple one.

419 ⟨∗2ekernel | def1⟩

420 \gdef\tryif@simple#1-#2\tryif@simple{%

Most common case for \reserved@f first:

421 \let \reserved@f \try@simples
422 \if>#2%

If so, it compares it to the value of \f@size. This is done using a dimen register since there may be fractional numbers.

423  \dimen@ #1\p@
424  \ifdim \dimen@<\@M\p@

If \dimen@ is \@M\p@ we have reached the end of the fontspec (hopefully) otherwise we compare the value with \f@size and compute in \@tempdimc the absolute value of the difference between the two values.

425  \ifdim \f@size\p@<\dimen@
426  \@tempdimc \dimen@
427  \advance\@tempdimc -\f@size\p@
428  \else
429  \@tempdimc \f@size\p@
430  \advance\@tempdimc -\dimen@
431  \fi

The result is then compared with the smallest difference we have encountered, if the new value (in \@tempdimc is smaller) we have found a size which is a better approximation so we make it the \best@size and adjust \@tempdimb.

432  \ifdim \@tempdimc<\@tempdimb
433  \@tempdimb \@tempdimc
434  \def \best@size{#1}%
435  \fi

When we have reached the end of the fontspec we substitute the best size found (if any). We code this inline to save macro space; in the past this was done by a macro called \subst@size.

436  \else

\subst@size This macro substitutes the size recorded in \best@size for the unavailable size \f@size. \font@submax records the maximum difference between desired size and selected size in the whole run.

437 % %\subst@size             %% coded inline
438 % %\def\subst@size{%
439 \ifx \external@font\@empty
440  \ifx \best@size\@empty
441  \else
442  \ifdim \@tempdimb>\font@submax \relax
443  \xdef \font@submax {\the\@tempdimb}%
444  \fi
445  \let \f@user@size \f@size
446  \let \f@size \best@size
447  \ifdim \@tempdimb>\fontsubfuzz\relax
448  \@font@warning{Font\space shape\space
449  `\curr@fontshape'\space in\space size\space
450  <\f@user@size>\space not\space available\MessageBreak
451  size\space <\f@size>\space substituted}%
452  \fi
453  \try@simple@size
454  \do@subst@correction

455  \fi
456 \fi
457 % %}

This brings us back into the main part of \tryif@simple. Finally we get rid of any rubbish left over on the input stack.

458  \let \reserved@f \remove@to@nnil
459  \fi
460 \fi

If it’s a range iterate also.

461 \reserved@f}

462 ⟨/2ekernel | def1⟩

6.1 Sizefunctions

In the following we define some useful size functions.

\s@fct@ This is the default size function. Mandatory argument is an external font name, optinal argument a scale factor. The font is scaled to \f@size if no optional argument is present, and to \f@size multiplied by the optional argument otherwise.

463 ⟨∗2ekernel | autoload⟩

464 \DeclareSizeFunction{}{\empty@sfcnt\@font@warning}
465 \DeclareSizeFunction{s}{\empty@sfcnt\@font@info}

466 ⟨/2ekernel | autoload⟩

467 ⟨∗2ekernel | def2 | autoload⟩

468 \def\empty@sfcnt#1{%
469  \@tempdimb \f@size\p@
470  \ifx\optional@arg\@empty
471  \else
472  \@tempdimb \optional@arg\@tempdimb
473  #1{Font\space shape\space `\curr@fontshape'\space
474  will\space be\MessageBreak
475  scaled\space to\space size\space \the\@tempdimb}%
476  \fi
477  \edef\external@font{\mandatory@arg\space at\the\@tempdimb}}

478 ⟨/2ekernel | def2 | autoload⟩

\s@fct@gen \s@fct@sgen This size function generates the external name from the mandatory argument and the requested user size, and thus can be used for external names where the size is encoded in the font name. The optinal argument a scale factor. The font is scaled to \f@size if no optional argument is present, and to \f@size multiplied by the optional argument otherwise.

479 ⟨∗2ekernel | autoload⟩

480 \DeclareSizeFunction{gen}{\gen@sfcnt\@font@warning}
481 \DeclareSizeFunction{sgen}{\gen@sfcnt\@font@info}

482 ⟨/2ekernel | autoload⟩ 483 ⟨∗2ekernel | def2 | autoload⟩

484 \def\gen@sfcnt{%
485  \edef\mandatory@arg{\mandatory@arg\f@size}%
486  \empty@sfcnt}

487 ⟨/2ekernel | def2 | autoload⟩

\s@fct@genb \s@fct@sgenb This size function is similar to gen, but for fonts where the size is encoded in the font name in centipoints, as in the DC fonts version 1.2. The font is scaled to \f@size if no optional argument is present, and to \f@size multiplied by the optional argument otherwise.

488 ⟨∗2ekernel | autoload⟩

489 \DeclareSizeFunction{genb}{\genb@sfcnt\@font@warning}
490 \DeclareSizeFunction{sgenb}{\genb@sfcnt\@font@info}

491 ⟨/2ekernel | autoload⟩

492 ⟨∗2ekernel | def2 | autoload⟩

493 \def\genb@sfcnt{%
494  \edef\mandatory@arg{\mandatory@arg\expandafter\genb@x\f@size..\@@}%
495  \empty@sfcnt}

496 ⟨/2ekernel | def2 | autoload⟩

\genb@x \genb@y The auxiliary macros \genb@x and \genb@y are used to convert the \f@size into centipoints.

497 ⟨∗2ekernel | def2 | autoload⟩

498 \def\genb@x#1.#2.#3\@@{\two@digits{#1}\genb@y#200\@@}
499 \def\genb@y#1#2#3\@@{#1#2}

500 ⟨/2ekernel | def2 | autoload⟩

\s@fct@sub This size function handles font substitution. The mandatory argument is a family/series/shape combination, the optional argument (if present) is ignored. The font encoding scheme cannot be changed. Therefore, the first thing we do is to prepend the encoding scheme.

501 ⟨∗2ekernel | autoload⟩

502 \DeclareSizeFunction{sub}{\sub@sfcnt\@font@warning}
503 \DeclareSizeFunction{ssub}{\sub@sfcnt\@font@info}

504 ⟨/2ekernel | autoload⟩

505 ⟨∗2ekernel | def2 | autoload⟩

506 \def\sub@sfcnt#1{%
507  \edef\mandatory@arg{\f@encoding/\mandatory@arg}%

Next action is split the arg into its individual components and allow for a late font shape load.

508  \begingroup
509  \expandafter\split@name\mandatory@arg/\@nil
510  \try@load@fontshape
511  \endgroup

Then we record the current \f@size since it may get clobbered.

512  \let\f@user@size\f@size

Then we check whether this new combination is defined and give an error message if not. In this case we also switch to \error@fontshape.

513  \expandafter
514  \ifx\csname\mandatory@arg\endcsname\relax
515  \errmessage{No\space declaration\space for\space
516  shape\space \mandatory@arg}%
517  \error@fontshape
518  \else

Otherwise we warn the user about the substitution taking place.

519  #1{Font\space shape\space `\curr@fontshape'\space in\space
520  size\space <\f@size>\space not\space available\MessageBreak
521  Font\space shape\space `\mandatory@arg'\space tried\space
522  instead}%
523  \expandafter\split@name\mandatory@arg/\@nil
524  \fi

Then we restart the font specification scan by calling \get@external@font.

525  \edef\f@size{\f@user@size}%
526  \get@external@font

Finally \do@subst@correction is called to get the font name right.

527  \do@subst@correction

528 } 529 ⟨/2ekernel | def2 | autoload⟩

\s@fct@subf The subf size function allows substitution of another font. The mandatory argument is the external name of the font to be substituted, the optional argument a size scaling factor like in the default size function. The main difference to the default size function is the warning message.

530 ⟨∗2ekernel | autoload⟩

531 \DeclareSizeFunction{subf}{\subf@sfcnt\@font@warning}
532 \DeclareSizeFunction{ssubf}{\subf@sfcnt\@font@info}

533 ⟨/2ekernel | autoload⟩

534 ⟨∗2ekernel | def2 | autoload⟩

535 \def\subf@sfcnt#1{%
536  #1{Font\space shape\space `\curr@fontshape'\space in\space
537  size\space \f@size\space not\space available\MessageBreak
538  external\space font\space `\mandatory@arg'\space used}%
539  \empty@sfcnt#1%

540 } 541 ⟨/2ekernel | def2 | autoload⟩

\s@fct@fixed The fixed size function is for using a font at a different size than requested. A warning message is printed, and the external font to be used is taken from the mandatory argument. If an optional argument is present it is used as the ‘at’ size for the font. Otherwise the font is loaded at its design size.

542 ⟨∗2ekernel | autoload⟩

543 \DeclareSizeFunction{fixed}{\fixed@sfcnt\@font@warning}

544 \DeclareSizeFunction{sfixed}{\fixed@sfcnt\@font@info}

545 ⟨/2ekernel | autoload⟩

546 ⟨∗2ekernel | def2 | autoload⟩

547 \def\fixed@sfcnt#1{%
548 \ifx\optional@arg\@empty
549  \let\external@font\mandatory@arg
550 \else
551  \edef\external@font{\mandatory@arg\space at\optional@arg pt}%
552 \fi
553 #1{External\space font\space `\external@font'\space loaded\space
554  for\space size\MessageBreak
555  <\f@size>}%

556 } 557 ⟨/2ekernel | def2 | autoload⟩