fattr.tex 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. % font set Computer Modern, Latin Modern, Bera
  2. % font family CMRoman, CMTypewriter, CMSansSerif
  3. %
  4. % font feature slant, weight, width
  5. % font attribute sl, it, bold, cond
  6. %
  7. % fontfile
  8. %
  9. % Font features and attributes:
  10. %
  11. % size design point size
  12. % encoding ot1, oml, oms, omx, t1, ts1, t2a
  13. % slant up, sl, it, ui, cursive
  14. % weight lt, med, semib, bold, bx,
  15. % width cond, normal, ext
  16. % figures oldfigures, liningfigures
  17. % caps normalcaps, allcaps, smallcaps, nocaps
  18. \catcode`@=11
  19. %
  20. % \fsz:CMRoman/<OT1>/<up>/<med>/<normal>/<liningfigures>/<normalcaps>
  21. % ->
  22. % cmr5/5,cmr7/7,cmr8/8,cmr9/9,cmr10/10,cmr12/12,cmr17/17,.
  23. %
  24. % {CMRoman}{b}{bx}
  25. % {OML,CMRoman}{m}{up}
  26. % {OML,CMRoman}{bx}{up}
  27. % {OML,CMRoman}{b}{bx}
  28. % {CMSans}{it}{sl}
  29. % {CMSans}{b}{bx}
  30. % {CMSans,OML}{}{CMRoman}
  31. % {ConcreteRoman}{b}{LMSans,b}
  32. % {OT1,CMMath}{}{CMRoman,n}
  33. % {OMX,CMMath}{bx}{m}
  34. %
  35. \input eplain
  36. %
  37. %
  38. %
  39. % Font-related logging. Meanings of \ftracelevel values are:
  40. %
  41. % 0 - none
  42. % 1 - warning
  43. % 2 - debug
  44. % 3 - verbose
  45. \newcount\ftracelevel
  46. \ftracelevel=1
  47. %
  48. \def\f@warning{\f@trace0}%
  49. \def\f@debug {\f@trace1}%
  50. \def\f@verbose{\f@trace2}%
  51. \def\f@trace#1{%
  52. \ifnum#1<\ftracelevel
  53. \expandafter\message
  54. \else
  55. \expandafter\gobble
  56. \fi
  57. }%
  58. %
  59. %
  60. %
  61. % Defining new fonts.
  62. %
  63. % \newfont MAG-FACTOR FILE-NAME DESIGN-SIZE [ATTRIBUTES]
  64. %
  65. % If ATTRIBUTES is not given, will reuse ATTRIBUTES from the
  66. % last \newfont call with ATTRIBUTES given. To define a font with
  67. % empty attributes, pass \empty or {} for ATTRIBUTES.
  68. \let\newfont@last@attributes\empty
  69. %
  70. {\catcode`\^^M=12 % Comment out all line ends from now on.
  71. \gdef\newfont{\begingroup \catcode`\^^M=12 \@newfont}%
  72. \gdef\@newfont#1 #2 #3^^M{\endgroup%
  73. \def\newfont@mag{#1}%
  74. \def\newfont@fname{#2}%
  75. \@new@font#3 ^^M%
  76. }%
  77. \gdef\@new@font #1 #2^^M{% DESIGN-SIZE [ATTRIBUTES]
  78. \get@dimen{#1}% Convert font size into a dimen.
  79. \if^^M#2^^M% Assume #2 contains no (leading) ^^Ms.
  80. % #2 is empty, continue to use the current \newfont@last@attributes.
  81. \else%
  82. \@new@font@#2^^M% Redefine \newfont@last@attributes, removing the space.
  83. \fi%
  84. \def@newfont%
  85. }%
  86. \gdef\@new@font@#1 ^^M{\def\newfont@last@attributes{#1}}%
  87. }%
  88. %
  89. %\def\def@newfont{%
  90. % \message{^^J.\newfont@mag.\newfont@fname.\the\dimen@.\newfont@last@attributes.}%
  91. %}%
  92. %
  93. \def\def@newfont{%
  94. % Empty \ff@temp0, \ff@temp1, ..., \ff@temp<MAX-ITEM-IDX>.
  95. \ff@temp@reset
  96. \for\ff@temp@i:=\newfont@last@attributes\do{%
  97. \f@get@af\ff@temp@i
  98. \expandafter\edef\csname ff@temp\tempb\endcsname{\tempa\space}%
  99. }%
  100. % Combine all \f@temp<X> into \f@name.
  101. \edef\f@name{f:\ff@temp@collect}%
  102. \expandafter\let\expandafter\temp \csname \f@name \endcsname
  103. %
  104. \ifx\temp\relax
  105. \let\do\relax
  106. \expandafter\xdef\csname\f@name\endcsname
  107. {\do \the\dimen@ \space \newfont@mag\space {\newfont@fname}}%
  108. \f@debug{^^J\the\inputlineno: Started font def with
  109. "\csname \f@name\endcsname".}%
  110. \else
  111. \edef\newfont@size{\the\dimen@}%
  112. \divide\dimen@ by1000
  113. \multiply\dimen@ by\newfont@mag\relax
  114. \let\newfont@prefix\empty
  115. \let\do\newfont@insert
  116. % Try inserting the new size.
  117. \temp\relax
  118. % If the new size hasn't been inserted, append it now.
  119. \ifx\do\newfont@insert
  120. \let\do\relax
  121. \expandafter\xdef\csname\f@name\endcsname {%
  122. \newfont@prefix \do \newfont@size\space \newfont@mag\space {\newfont@fname}%
  123. }%
  124. \f@debug{^^J\the\inputlineno: Updated font def to
  125. "\csname \f@name\endcsname".}%
  126. \fi
  127. \fi
  128. }%
  129. %
  130. % \newfont@insert FONT-SIZE MAG-FACTOR FILE-NAME
  131. %
  132. % Insert new font file into the list of font files in \newfont@prefix,
  133. % preserving ascending order of scaled design sizes.
  134. %
  135. % Pass in new font's design size in \newfont@size, mag factor
  136. % in \newfont@mag, font file name in \newfont@fname, pre-computed
  137. % scaled size in \dimen@.
  138. \def\newfont@insert#1 #2 #3{%
  139. \toks@=\expandafter{\newfont@prefix}%
  140. % We are comparing scaled font sizes, not design sizes.
  141. \dimen@ii=#1\divide\dimen@ii by1000 \multiply\dimen@ii by#2\relax
  142. %
  143. \ifdim \dimen@ii < \dimen@
  144. % Not inserting yet, go on to the next font file.
  145. \edef\newfont@prefix{\the\toks@ \noexpand\do #1 #2 {#3}}%
  146. \else
  147. % Time to insert the new font file. If we are adding a font file
  148. % for the same design size/mag factor, the new def will override
  149. % the old, but give out a warning.
  150. \def\newfont@next{\noexpand\do #1 #2 {#3}}%
  151. \ifnum #2=\newfont@mag
  152. \ifdim #1=\newfont@size
  153. \f@warning{^^J\the\inputlineno: Warning: Replacing font "#3" (#1@#2)
  154. with font "\newfont@fname" (\newfont@size @\newfont@mag).}%
  155. \let\newfont@next\empty
  156. \fi
  157. \fi
  158. \edef\newfont@prefix{\the\toks@
  159. \noexpand\do \newfont@size \space \newfont@mag \space {\newfont@fname}%
  160. \newfont@next}%
  161. % No need to parse further, just append the rest of the list.
  162. \expandafter\newfont@append
  163. \fi
  164. }%
  165. %
  166. \def\newfont@append#1\relax{%
  167. \let\do\relax
  168. \expandafter\xdef\csname\f@name\endcsname {\newfont@prefix #1}%
  169. \f@debug{^^J\the\inputlineno: Updated font def to
  170. "\csname \f@name\endcsname".}%
  171. }%
  172. %
  173. %
  174. %
  175. % Defining new font features and attributes.
  176. %
  177. \newcount\fcacheidx % Font cache index.
  178. \newcount\ffeatcount % Font feature count.
  179. % We'll build up the following as we add font features.
  180. \let\ff@reset\empty
  181. \let\ff@collect\empty
  182. \let\ff@temp@reset\empty
  183. \let\ff@temp@collect\empty
  184. %
  185. % \newfontattr FONTFEATURE FONTATTR
  186. \def\newfontattr #1 #2 {%
  187. % Define a new font attribute, if it's not defined yet.
  188. \expandafter\ifx\csname fa:#2\endcsname \relax
  189. \else
  190. \errmessage{Font attribute "#2" already defined as part
  191. of font feature "\csname ff@\csname faf:#2\endcsname\endcsname"}%
  192. \fi
  193. \expandafter\xdef\csname fa:#2\endcsname{\the\fcacheidx}%
  194. \expandafter\xdef\csname fa@\the\fcacheidx\endcsname{#2}%
  195. % Invalidate current font cache (and update index for the next font
  196. % attribute).
  197. \global\advance\fcacheidx by1
  198. %
  199. % Define a new font feature, if it's not defined yet.
  200. \expandafter\ifx\csname ff:#1\endcsname \relax
  201. \expandafter\xdef\csname ff:#1\endcsname{\the\ffeatcount}%
  202. \expandafter\xdef\csname ff@\the\ffeatcount\endcsname{#1}%
  203. % Update \ff@reset to clear the new font feature cell \ffN.
  204. \toks@=\expandafter{\ff@reset}%
  205. \xdef\ff@reset{\the\toks@
  206. \let\expandafter\noexpand\csname ff\the\ffeatcount\endcsname
  207. \noexpand\empty}%
  208. % Update \ff@temp@reset to clear the new font feature cell \ff@tempN.
  209. \toks@=\expandafter{\ff@temp@reset}%
  210. \xdef\ff@temp@reset{\the\toks@
  211. \let\expandafter\noexpand\csname ff@temp\the\ffeatcount\endcsname
  212. \noexpand\empty}%
  213. % Update \ff@collect to include the new font feature cell \ffN.
  214. \toks@=\expandafter{\ff@collect}%
  215. \xdef\ff@collect{\the\toks@
  216. ^^A\expandafter\noexpand\csname ff\the\ffeatcount\endcsname}%
  217. % Update \ff@temp@collect to include the new font feature cell \ffN.
  218. \toks@=\expandafter{\ff@temp@collect}%
  219. \xdef\ff@temp@collect{\the\toks@
  220. ^^A\expandafter\noexpand\csname ff@temp\the\ffeatcount\endcsname}%
  221. % Set \ffN to \empty, otherwise it will be set to \relax the first
  222. % time we try to access it through \csname...\endcsname, and we
  223. % depend on it to be either a number or \empty.
  224. \global\expandafter\let\csname ff\the\ffeatcount\endcsname \empty
  225. %
  226. \global\advance\ffeatcount by1
  227. % We've added a new font feature, so we should invalidate current
  228. % font cache. But we've already done so above when adding the new
  229. % font attribute.
  230. %\global\advance\fcacheidx by1
  231. \fi
  232. % Assign the font attribute to the font feature.
  233. \expandafter\xdef\csname faf:#2\endcsname{\csname ff:#1\endcsname}%
  234. }%
  235. %
  236. % \newfontattrs FONTFEATURE FONTATTRIBUTE[,...]
  237. \def\newfontattrs #1 #2 {%
  238. \for\temp:=#2\do{\newfontattr #1 {\temp} }%
  239. }%
  240. %
  241. %
  242. %
  243. % Setting a font.
  244. %
  245. \newcount\f@base@mag \f@base@mag=1000
  246. \newcount\f@count@
  247. % Set a font based on the current values of \ff0, \ff1, ..., or
  248. % execute #1 if such font is not defined.
  249. \def\f@setfont#1{%
  250. \f@verbose{^^J\the\inputlineno: (Started search for a font.}%
  251. % Check the cache.
  252. \expandafter\let\expandafter\temp
  253. \csname f\the\fcacheidx:\the\f@cur@size:\ff@collect\endcsname
  254. \ifx\temp\relax
  255. % Not in the cache.
  256. \let\do\relax
  257. \expandafter\let\expandafter\temp \csname f:\ff@collect\endcsname
  258. \ifx\temp\relax
  259. \f@debug{^^JFont is not defined.}%
  260. #1% Execute the no-font action.
  261. \else
  262. \dimen@=\z@ % Fake previous size for the first font in the list.
  263. \let\f@fname\empty
  264. \let\do\f@search@size
  265. \global\dimen@i=\f@cur@size
  266. \temp\relax
  267. \ifnum\f@base@mag=\f@count@ \else
  268. \global\divide\dimen@i by\f@base@mag
  269. \global\multiply\dimen@i by\f@count@
  270. \fi
  271. \f@debug{Found font "\f@fname", mag factor \the\f@count@,
  272. scaled size \the\dimen@i.}%
  273. % Save it in the cache.
  274. \global \expandafter\font
  275. \csname f\the\fcacheidx:\the\f@cur@size:\ff@collect\endcsname
  276. \f@fname \space at\the\dimen@i
  277. % Set it.
  278. \csname f\the\fcacheidx:\the\f@cur@size:\ff@collect\endcsname
  279. \fi
  280. \else
  281. % Got a cache hit.
  282. \f@debug{Found font in the cache.}%
  283. \temp
  284. \fi
  285. \f@verbose{Ended search for a font.)}%
  286. }%
  287. %
  288. % \f@search@size DESIGN-SIZE MAG-FACTOR FILE-NAME
  289. %
  290. % Pass in the desired font size in \dimen@i. Font file name is saved
  291. % in \f@fname, font's mag factor in \f@count@.
  292. \def\f@search@size #1 #2 #3{%
  293. \f@verbose{Looking at font def "#3" (#1@#2).}%
  294. \dimen@ii=\dimen@ % Get the size of the previous font from the list.
  295. % Scale design size of the next font as per current base mag factor.
  296. \dimen@=#1\relax
  297. \ifnum#2=\f@base@mag \else
  298. \divide\dimen@ by\f@base@mag \multiply\dimen@ by#2\relax
  299. \fi
  300. % Calculate the "dividing" point size.
  301. \count@=\dimen@ % Don't clobber \dimen@.
  302. \advance\count@ by-\dimen@ii % Curr size - prev size.
  303. % Don't do the following line -- .3333*3pt < 1pt.
  304. %\advance\dimen@ii by.3333\count@ % 1/3 of the way from prev to curr.
  305. \divide\count@ by3
  306. \advance\dimen@ii by\count@ sp% 1/3 of the way from prev to curr.
  307. %
  308. \ifdim \dimen@i > \dimen@ii
  309. % The previous font's size is too small. The current font might
  310. % or might not be the one we need, but we assume it is, in case
  311. % it's the last one in the list.
  312. \def\f@fname{#3}%
  313. \f@count@=#2\relax
  314. \else
  315. % The target size is close enough to the prev font's size, so we take that.
  316. \ifx\f@fname\empty
  317. % This is the case when we need a size smaller than the very
  318. % first font in the list.
  319. \def\f@fname{#3}%
  320. \f@count@=#2\relax
  321. \fi
  322. \expandafter\f@search@gobble
  323. \fi
  324. }%
  325. %
  326. \def\f@search@gobble#1\relax{}%
  327. %
  328. %
  329. %
  330. % Font feature manipulations.
  331. %
  332. \newdimen\f@cur@size \f@cur@size=10pt
  333. % \setfont{ATTRS}
  334. \def\setfont{%
  335. % Empty \ff0, \ff1, ..., \ff<ffeature_count - 1>.
  336. \ff@reset
  337. \addfontattrs % Substitutes and sets the font.
  338. }%
  339. %
  340. % \modfont{REM-FEATURES}{ADD-OR-MOD-ATTRS}
  341. \def\modfont#1{%
  342. \unsetfontfeatures{#1}%
  343. \addfontattrs % Substitutes and sets the font.
  344. }%
  345. %
  346. % \addfontattrs{ADD-OR-MOD-ATTRS}
  347. \def\addfontattrs#1{%
  348. % For each feature with attribute in #1, set \ff<f> to `<a> ', where
  349. % <f> is the feature index and <a> is the attribute index. For a
  350. % number or a dimen in #1, set \f@cur@size. Dimen specs starting
  351. % with '.' (e.g., '.1pt') are not supported.
  352. \for\f@i:=#1\do{%
  353. \expandafter\gobble@to@finish % Gobbles 'pt' when \f@i is '10pt'.
  354. \ifnum9<1\f@i\relax
  355. \finish
  356. % A number or a dimen.
  357. \get@dimen{\f@i}%
  358. \f@cur@size=\dimen@
  359. \else
  360. \finish
  361. % Not a number.
  362. \f@get@af\f@i
  363. \expandafter\edef\csname ff\tempb\endcsname {\tempa\space}%
  364. \fi
  365. }%
  366. % Try to set the font; if it's not defined, do substitution and try
  367. % to set the resulting font. Do font substitution inside a group so
  368. % that the current set of font attributes is not clobbered.
  369. \f@setfont{{\f@subst \f@setfont{\f@err@nofont}\expandafter}\the\font}%
  370. }%
  371. %
  372. % Take a dimension or a number, and save it in \dimen@. In case of a
  373. % number, assume `pt' units.
  374. \def\get@dimen#1{%
  375. \afterassignment\gobble@to@finish
  376. \dimen@#1pt \finish
  377. }%
  378. \def\gobble@to@finish#1\finish{}%
  379. %
  380. \def\f@err@nofont{%
  381. \dumpfontfeatures
  382. \errmessage{^^JFound no font def for the selected feature set}%
  383. }%
  384. %
  385. % \remfontfeatures{REM-FEATURES}
  386. \def\remfontfeatures#1{%
  387. \unsetfontfeatures{#1}%
  388. % Try to set the font; if it's not defined, do substitution and try
  389. % to set the resulting font. Do font substitution inside a group so
  390. % that the current set of font attributes is not clobbered.
  391. \f@setfont{{\f@subst \f@setfont{\f@err@nofont}\expandafter}\the\font}%
  392. }%
  393. %
  394. \def\unsetfontfeatures#1{%
  395. % Unset \ff<f> for each feature <f> in #1.
  396. \for\f@i:=#1\do{%
  397. \expandafter\let\expandafter\temp\csname ff:\f@i\endcsname
  398. \ifx\temp\relax
  399. \errmessage{Undefined font feature "\f@i"}%
  400. \fi
  401. \expandafter\let \csname ff\temp\endcsname \empty
  402. }%
  403. }%
  404. %
  405. % Pretty-print the current settings of font features.
  406. \def\dumpfontfeatures{%
  407. \message{^^J\the\inputlineno: font size \the\f@cur@size, features (}%
  408. \@dumpfontfeatures\message
  409. \message{).}%
  410. }%
  411. %
  412. \def\@dumpfontfeatures#1{%
  413. \fori0\ffeatcount{%
  414. \edef\temp{\csname ff\the\count@\endcsname}%
  415. #1{%
  416. \csname ff@\the\count@\endcsname=%
  417. \ifx\temp\empty
  418. <unset>%
  419. \else
  420. \expandafter\dump@ff\temp
  421. \fi
  422. }%
  423. }%
  424. }%
  425. %
  426. \def\dump@ff#1 {\csname fa@#1\endcsname}%
  427. %
  428. %
  429. %
  430. % Construction of font filter strings.
  431. %
  432. % \fontsubstpre =MATCH-ATTR -REM-FEATURES +ADD-OR-MOD-ATTRS
  433. %
  434. % Install a new font substitution to be applied before other font
  435. % substitutions.
  436. \def\fontsubstpre{%
  437. \let\fontsubst@mklist\fontsubst@pre
  438. \@fontsubst
  439. }%
  440. %
  441. % \fontsubstpost =MATCH-ATTR -REM-FEATURES +ADD-OR-MOD-ATTRS
  442. %
  443. % Install a new font substitution to be applied after other font
  444. % substitutions.
  445. \def\fontsubstpost{%
  446. \let\fontsubst@mklist\fontsubst@post
  447. \@fontsubst
  448. }%
  449. %
  450. % \@fontsubst =MATCH-ATTR -REM-FEATURES +ADD-OR-MOD-ATTRS
  451. \def\@fontsubst{%
  452. % Construct the match string in \fontsubst@match@list.
  453. \f@mk@falist\fontsubst@\fontsubst@match@list % =MATCH-ATTR
  454. }%
  455. %
  456. % \fontsubst@ -REM-FEATURES +ADD-OR-MOD-ATTRS
  457. \def\fontsubst@{%
  458. % Construct the rem string in \fontsubst@rem@list.
  459. \f@mk@flist\fontsubst@@\fontsubst@rem@list % -REM-FEATURES
  460. }%
  461. %
  462. % \fontsubst@ +ADD-OR-MOD-ATTRS
  463. \def\fontsubst@@{%
  464. % Construct the add string in \fontsubst@add@list.
  465. \f@mk@falist\fontsubst@fin\fontsubst@add@list% +ADD-OR-MOD-ATTRS
  466. }%
  467. %
  468. \def\fontsubst@fin{%
  469. % Add the new substitution to either head or tail of the current
  470. % substitution list.
  471. \let\@end\relax
  472. \edef\f@subst@list{\fontsubst@mklist}%
  473. }%
  474. %
  475. \def\fontsubst@pre{%
  476. \fontsubst@match@list \@end
  477. \fontsubst@rem@list \@end
  478. \fontsubst@add@list \@end
  479. \f@subst@list
  480. }%
  481. %
  482. \def\fontsubst@post{%
  483. \f@subst@list
  484. \fontsubst@match@list \@end
  485. \fontsubst@rem@list \@end
  486. \fontsubst@add@list \@end
  487. }%
  488. %
  489. % Initialize the font substitution list to empty.
  490. \let\f@subst@list\empty
  491. %
  492. % Given the name of an attribute, return index of the attribute
  493. % in \tempa and index of the attribute's feature in \tempb.
  494. \def\f@get@af#1{%
  495. % Get attribute's index.
  496. \expandafter\let\expandafter\tempa\csname fa:#1\endcsname
  497. \ifx\tempa\relax
  498. \errmessage{Undefined font attribute "#1"}%
  499. \fi
  500. % Get feature index for the attribute.
  501. \expandafter\let\expandafter\tempb\csname faf:#1\endcsname
  502. \ifx\tempb\relax
  503. \errmessage{Undefined font attribute "#1"}%
  504. \fi
  505. }%
  506. %
  507. % Given a comma-separated list of attributes #4, construct a string
  508. % (saving it in macro #2) as a sequence of `F.A,' specs, where F is
  509. % the index of the feature to which attribute belongs, and A is the
  510. % index of the attribute. After that, call #1. #3 is an ignored
  511. % syntax sugar ("=" or "+") from the user-visible command.
  512. \def\f@mk@falist#1#2#3#4 {%
  513. \let#2\empty % Start with a clean slate.
  514. \let\do\relax
  515. \for\f@i:=#4\do{%
  516. \f@get@af\f@i
  517. \edef#2{#2\do\tempb.\tempa,}% Append the spec to the list.
  518. }%
  519. #1%
  520. }%
  521. %
  522. % Given a comma-separated list of features #4, construct a string
  523. % (saving it in macro #2) as a sequence of `F,' specs, where F is the
  524. % feature index. After that, call #1. After that, call #1. #3 is an
  525. % ignored syntax sugar ("-") from the user-visible command.
  526. \def\f@mk@flist#1#2#3#4 {%
  527. \let#2\empty
  528. \let\do\relax
  529. \for\f@i:=#4\do{%
  530. \expandafter\let\expandafter\temp\csname ff:\f@i\endcsname
  531. \ifx\temp\relax
  532. \errmessage{Undefined font feature "\f@i"}%
  533. \fi
  534. \edef#2{#2\do\temp,}%
  535. }%
  536. #1%
  537. }%
  538. %
  539. % \fori{FROM-INCL}{TO-EXCL}{EXEC}
  540. \def\fori#1#2#3{%
  541. \count@=#1\relax
  542. \loop
  543. #3\relax
  544. \advance\count@ by1
  545. \ifnum\count@<#2\repeat
  546. }%
  547. %
  548. %
  549. %
  550. % Generic filter parsing macros. Configure by defining these
  551. % callbacks (before running \f@run@filter on the filter string):
  552. %
  553. % \f@do@filter@match#1.#2, - match the pair filter/attribute.
  554. % \f@do@filter@rem - unset feature `F,'.
  555. % \f@do@filter@add - add the pair `F.A,'.
  556. % \f@hook@filter@end - action at the end of the filter (upon
  557. % successful match).
  558. % \f@hook@filter@skip - action for \f@filter@gobble@this
  559. % and \f@filter@gobble@all.
  560. %
  561. % To skip one filter, \f@do@filter@match can
  562. % call \f@filter@gobble@this. To skip all remaining filters,
  563. % call \f@filter@gobble@all.
  564. %
  565. % Note: Theoretically, we can use a construct like
  566. %
  567. % \csname ff@\csname faf:A\endcsname\endcsname
  568. %
  569. % to get the feature corresponding to attribute A. But this would
  570. % fail with an incomprehensible error message (`missing \endcsname')
  571. % if \csname faf:A\endcsname is undefined, so we'd have to test this
  572. % before each use. To avoid the overhead, we just add the font
  573. % feature index to the font filter.
  574. %
  575. %
  576. % \f@run@filter
  577. % [MATCH-ATTR\@end REM-FEATURES\@end ADD-OR-MOD-ATTRS\@end [...]]\relax
  578. \def\f@run@filter{%
  579. \let\do\f@do@filter@match
  580. \let\@end\f@run@filter@rem
  581. }%
  582. %
  583. % \f@run@filter@rem REM-FEATURES\@end ADD-OR-MOD-ATTRS\@end ... \relax
  584. \def\f@run@filter@rem{%
  585. \let\do\f@do@filter@rem
  586. \let\@end\f@run@filter@add
  587. }%
  588. %
  589. % \f@run@filter@add ADD-OR-MOD-ATTRS\@end ... \relax
  590. \def\f@run@filter@add{%
  591. \let\do\f@do@filter@add
  592. \let\@end\f@hook@filter@end
  593. }%
  594. %
  595. \def\f@filter@gobble@this#1\@end#2\@end#3\@end{\f@hook@filter@skip \f@run@filter}%
  596. \def\f@filter@gobble@all#1\relax{\f@hook@filter@skip}%
  597. %
  598. %
  599. %
  600. % Filter parsing callbacks for font substitution.
  601. %
  602. % Apply only the first font substitution matching the current font.
  603. \def\f@subst@once{%
  604. \let\f@do@filter@match\f@subst@match@init
  605. \let\f@do@filter@rem\f@subst@rem
  606. \let\f@do@filter@add\f@subst@add
  607. \let\f@hook@filter@end\f@subst@nomore
  608. \let\f@hook@filter@skip\f@subst@skip
  609. \expandafter\f@run@filter \f@subst@list \relax
  610. }%
  611. %
  612. % Apply all font substitutions in order, allowing substitutions to be
  613. % chained.
  614. \def\f@subst{%
  615. \let\f@do@filter@match\f@subst@match@init
  616. \let\f@do@filter@rem\f@subst@rem
  617. \let\f@do@filter@add\f@subst@add
  618. \let\f@hook@filter@end\f@subst@again
  619. \let\f@hook@filter@skip\f@subst@skip
  620. \f@verbose{^^J\the\inputlineno: (Running font substitution filter:}%
  621. \expandafter\f@run@filter \f@subst@list \relax
  622. \f@verbose{^^J)}%
  623. }%
  624. %
  625. % Match one feature.
  626. \def\f@subst@match@init{%
  627. \f@verbose{^^J(}%
  628. \let\do\f@subst@match
  629. \do
  630. }%
  631. %
  632. \def\f@subst@match#1.#2,{%
  633. \f@verbose{^^Jmatching \csname ff@#1\endcsname.\csname fa@#2\endcsname}%
  634. % This funky way to compare the two numbers takes care of \ff#1
  635. % being \empty. However, keep in mind that if \ff#1 is undefined,
  636. % the following will make it a \relax. Space at the end of \ff#1
  637. % will be gobbled by TeX, because it's a space following a number.
  638. \ifnum 1#2=1\csname ff#1\endcsname \else
  639. \f@verbose{^^J skipping, unmatched
  640. \csname ff@#1\endcsname.\csname fa@#2\endcsname}%
  641. \expandafter\f@filter@gobble@this % Skip to the next filter.
  642. \fi
  643. }%
  644. %
  645. % Remove one feature.
  646. \def\f@subst@rem#1,{%
  647. \f@verbose{^^Junsetting \csname ff@#1\endcsname}%
  648. \expandafter\let\csname ff#1\endcsname \empty
  649. }%
  650. %
  651. % Add attribute #2 (which must belong to feature #1).
  652. \def\f@subst@add#1.#2,{%
  653. \f@verbose{^^Jadding \csname ff@#1\endcsname.\csname fa@#2\endcsname}%
  654. \expandafter\def\csname ff#1\endcsname{#2 }%
  655. }%
  656. %
  657. \def\f@subst@nomore{\f@verbose{^^J)}\f@filter@gobble@all}%
  658. \def\f@subst@again{\f@verbose{^^J)}\f@run@filter}%
  659. \def\f@subst@skip{\f@verbose{^^J)}}%
  660. %
  661. %
  662. %
  663. % Filter parsing callbacks for pretty-printing the current font filter
  664. % string.
  665. %
  666. % Pretty-print the current font filter string.
  667. \def\dumpfontfilter{\f@dump@filter\f@subst@list}%
  668. %
  669. % Pretty-print the given font filter string.
  670. \def\f@dump@filter#1{%
  671. \let\f@do@filter@match\f@dump@match@init
  672. \let\f@do@filter@rem\f@dump@rem@init
  673. \let\f@do@filter@add\f@dump@add@init
  674. \let\f@hook@filter@end\f@dump@again
  675. \let\f@hook@filter@skip\relax
  676. \message{\the\inputlineno: (Dumping font substitution filter:}%
  677. \expandafter\f@run@filter #1\relax
  678. \message{^^J)}%
  679. }%
  680. \def\f@dump@match@init{%
  681. \message{^^J(^^J=}%
  682. \def\do##1.##2,{\message{\csname ff@##1\endcsname.\csname fa@##2\endcsname}}%
  683. \do
  684. }%
  685. \def\f@dump@rem@init{%
  686. \message{^^J-}%
  687. \def\do##1,{\message{\csname ff@##1\endcsname}}%
  688. \do
  689. }%
  690. \def\f@dump@add@init{%
  691. \message{^^J+}%
  692. \def\do##1.##2,{\message{\csname ff@##1\endcsname.\csname fa@##2\endcsname}}%
  693. \do
  694. }%
  695. \def\f@dump@again{\message{^^J)}\f@run@filter}%
  696. \endinput
  697. \ftracelevel=3
  698. \newfontattr family CMRoman
  699. \newfontattr family CMTypewriter
  700. \newfontattr family CMSansSerif
  701. \newfontattr encoding OT1
  702. \newfontattr encoding OML
  703. \newfontattr encoding OMS
  704. \newfontattr encoding OMX
  705. \newfontattr slant up
  706. \newfontattr slant sl
  707. \newfontattr slant it
  708. \newfontattr slant ui
  709. \fontsubstpre =CMRoman -slant +OT1
  710. %\dumpfontfilter
  711. \fontsubstpre =up - +CMRoman,OML
  712. %\dumpfontfilter
  713. \fontsubstpost =CMTypewriter -encoding +
  714. %\dumpfontfilter
  715. \fontsubstpost =CMRoman,up,OML -slant +OMX
  716. %\dumpfontfilter
  717. \fontsubstpre =it,CMTypewriter,OMX -family,encoding +OT1
  718. %\dumpfontfilter
  719. \fontsubstpost =CMSansSerif,ui -encoding +CMTypewriter,OMX,it
  720. %\dumpfontfilter
  721. \fontsubstpost =it,CMTypewriter,OMX -encoding,family +ui,OMS
  722. \dumpfontfilter
  723. \dumpfontfeatures
  724. %\newfont 10 cmbx10 {}
  725. %\message{^^J***\f@name}
  726. \newfont 10 1000 cmti10 CMRoman,it,OT1
  727. \message{^^J***\f@name}
  728. \newfont 10 1000 cmt10 CMRoman,OT1
  729. \message{^^J***\f@name}
  730. \newfont 12pt 1000 cmti12 CMRoman,it,OT1
  731. \message{^^J***\f@name}
  732. \newfont 10 1000 cmmi10 ui,OMS
  733. \message{^^J***\f@name}
  734. \newfont 8 1000 cmti8 CMRoman,it,OT1
  735. \message{^^J***\f@name}
  736. \newfont 11 1000 cmti11 CMRoman,it,OT1
  737. \message{^^J***\f@name}
  738. \newfont 8 1000 cmt8 CMRoman,OT1
  739. \message{^^J***\f@name}
  740. \newfont 9 1000 cmt9 CMRoman,OT1
  741. \message{^^J***\f@name}
  742. \newfont 0.2cm 1000 cmti1cm CMRoman,it,OT1
  743. \message{^^J***\f@name}
  744. \newfont 1.in 1000 cmin it,OT1
  745. \message{^^J***\f@name}
  746. \newfont 5. 1000 cmot1 OT1
  747. \message{^^J***\f@name}
  748. \setfont {CMRoman,it,0.3cm,OMS}% -> family=CMRoman encoding=OT1 slant=<unset>
  749. \dumpfontfeatures
  750. \setfont {CMTypewriter,it,OMX}% -> family=<unset> encoding=OT1 slant=it
  751. \dumpfontfeatures
  752. \addfontattrs {7,up}% -> family=CMRoman encoding=OT1 slant=<unset>
  753. \dumpfontfeatures
  754. \remfontfeatures{family}% -> family=<unset> encoding=OT1 slant=<unset>
  755. \dumpfontfeatures
  756. \modfont {}{CMSansSerif,ui}% -> family=<unset> encoding=OMS slant=ui
  757. \dumpfontfeatures
  758. \remfontfeatures{}% -> family=<unset> encoding=OMS slant=ui
  759. \dumpfontfeatures
  760. \addfontattrs {}% -> family=<unset> encoding=OMS slant=ui
  761. \dumpfontfeatures
  762. \modfont {}{}% -> family=<unset> encoding=OMS slant=ui
  763. \dumpfontfeatures
  764. \message{^^J*********************************************************}
  765. \newfontattr family TestFamily
  766. \newfont 5. 1000 sz5-1.0-1 TestFamily
  767. \message{^^J***\f@name}
  768. \newfont 5. 1000 sz5-1.0-2 TestFamily
  769. \message{^^J***\f@name}
  770. \newfont 5. 1400 sz5-1.4-1 TestFamily
  771. \message{^^J***\f@name}
  772. \newfont 5. 900 sz5-0.9-1 TestFamily
  773. \message{^^J***\f@name}
  774. \newfont 5. 900 sz5-0.9-2 TestFamily
  775. \message{^^J***\f@name}
  776. \newfont 5. 1400 sz5-1.4-2 TestFamily
  777. \message{^^J***\f@name}
  778. \setfont {CMRoman,9pt}% ->
  779. \newfont 10 1000 cmr10 CMRoman,rm,OT1
  780. \message{^^J***\f@name}
  781. \bye