在“元字体的概念”Donald E. Knuth 以视觉方式探索了参数变化的可能性。但我有一些疑问:
- 他是否使用 TeX 排版该文章?
- TeX 是否允许连续改变参数?有时,即使在同一段落中,字母之间的变化也是可见的。在这种情况下,Metafont 和 TeX 之间的通信如何处理?
谢谢
答案1
Knuth 的元字体的概念(抽象的,PDF)不仅仅是一篇论文,更是一场表演,因为他利用每个段落本身的视觉外观来阐释其中的思想。在两个特定段落之间有一个单独的页面,其中包含诗篇 23 的设置,这也标志着论文本身从一种字体到另一种字体的过渡:
这里,字符的宽度(“em width”)和高度(“x-height”)逐渐变大(即使在同一行内),同时保持相同的“h-height”(我认为是上升高度)。
该论文当然是用 TeX 排版的,包括这个插页。
为了展示如何做到这一点,同时保持简单(为了我自己,我对 Metafont 不是很熟悉),我将使用一种特别简单的字体,仅包含数字 0-9,从这个问题。
没有花招
只是重复另一个问题中的例子(只是包含在这里以便于理解后面的例子):您可以将以下字体的定义放在名为的文件中postal.mf
:
leftgap := 0.1;
rightgap := 0.1;
penthickness := 0.08;
midlineheight := 0.5;
toplineheight := 1;
width# := 9pt#;
height# := 10pt#;
mode_setup;
def begincchar(expr c) =
beginchar(c, width#, height#, 0);
t := toplineheight * h;
m := midlineheight * h;
a := leftgap * w; b := w - rightgap * w;
z1=(a,t); z2=(b,t);
z3=(a,m); z4=(b,m);
z5=(a,0); z6=(b,0);
pickup pencircle scaled (penthickness * w);
penlabels(1,2,3,4,5,6);
enddef;
def drawzero = draw z2--z1--z5--z6--cycle; endchar; enddef;
def drawone = draw z3--z2--z6; endchar; enddef;
def drawtwo = draw z1--z2--z4--z5--z6; endchar; enddef;
def drawthree = draw z1--z2--z3--z4--z5; endchar; enddef;
def drawfour = draw z1--z3--z4; draw z2--z6; endchar; enddef;
def drawfive = draw z2--z1--z3--z4--z6--z5; endchar; enddef;
def drawsix = draw z2--z3--z5--z6--z4--z3; endchar; enddef;
def drawseven = draw z1--z2--z3--z5; endchar; enddef;
def draweight = draw z2--z1--z5--z6--cycle; draw z3--z4; endchar; enddef;
def drawnine = draw z4--z3--z1--z2--z4--z5; endchar; enddef;
begincchar("0"); drawzero;
begincchar("1"); drawone;
begincchar("2"); drawtwo;
begincchar("3"); drawthree;
begincchar("4"); drawfour;
begincchar("5"); drawfive;
begincchar("6"); drawsix;
begincchar("7"); drawseven;
begincchar("8"); draweight;
begincchar("9"); drawnine;
end
然后在.tex
文件中使用该字体:
\font\postalfont=postal
$\pi$ is roughly three point {\postalfont 14159265} in value.
\bye
要得到:
手动定义字体
对于“耶和华是我的牧羊人”的例子,Knuth 定义了一种特殊的“字体”,其 256 个字符分别为“T(带有某些参数)”、“h(带有某些参数)”、“e(带有某些参数)”、“L(带有某些参数)”等等。(每 256 个字符后切换到一种新字体。)例如,第一行中的 3 个“e”只是同一字体中的 3 个不同字形。以下是与我们的邮政编码字体类似的示例。这次,删除上次(postal.*
)留下的任何内容后,将以下内容放入postal.mf
字体中:
leftgap := 0.1;
rightgap := 0.1;
penthickness := 0.08;
midlineheight := 0.5;
toplineheight := 1;
width# := 9pt#;
height# := 10pt#;
scale := 1;
mode_setup;
def begincchar(expr c) =
beginchar(c, width# * scale, height# * scale, 0);
t := toplineheight * h;
m := midlineheight * h;
a := leftgap * w; b := w - rightgap * w;
z1=(a,t); z2=(b,t);
z3=(a,m); z4=(b,m);
z5=(a,0); z6=(b,0);
pickup pencircle scaled (penthickness * w / scale);
penlabels(1,2,3,4,5,6);
enddef;
def drawzero = draw z2--z1--z5--z6--cycle; endchar; enddef;
def drawone = draw z3--z2--z6; endchar; enddef;
def drawtwo = draw z1--z2--z4--z5--z6; endchar; enddef;
def drawthree = draw z1--z2--z3--z4--z5; endchar; enddef;
def drawfour = draw z1--z3--z4; draw z2--z6; endchar; enddef;
def drawfive = draw z2--z1--z3--z4--z6--z5; endchar; enddef;
def drawsix = draw z2--z3--z5--z6--z4--z3; endchar; enddef;
def drawseven = draw z1--z2--z3--z5; endchar; enddef;
def draweight = draw z2--z1--z5--z6--cycle; draw z3--z4; endchar; enddef;
def drawnine = draw z4--z3--z1--z2--z4--z5; endchar; enddef;
begincchar(0); "1"; scale := scale*0.95; drawone;
begincchar(1); "4"; scale := scale*0.95; drawfour;
begincchar(2); "1"; scale := scale*0.95; drawone;
begincchar(3); "5"; scale := scale*0.95; drawfive;
begincchar(4); "9"; scale := scale*0.95; drawnine;
begincchar(5); "2"; scale := scale*0.95; drawtwo;
begincchar(6); "6"; scale := scale*0.95; drawsix;
begincchar(7); "5"; scale := scale*0.95; drawfive;
begincchar(8); "3"; scale := scale*0.95; drawthree;
begincchar(9); "5"; scale := scale*0.95; drawfive;
begincchar(10);"8"; scale := scale*0.95; draweight;
begincchar(11);"9"; scale := scale*0.95; drawnine;
begincchar(12);"7"; scale := scale*0.95; drawseven;
begincchar(13);"9"; scale := scale*0.95; drawnine;
begincchar(14);"3"; scale := scale*0.95; drawthree;
end
观察上面的最后部分,我们scale
在绘制字符之前进行了修改:早期的字体将数字“1”的字形放在与“1”的 ASCII 码对应的插槽中(即字体中的位置 49),就像普通字体一样。在这种情况下,我们改为将字体定义为在其最早的插槽(位置 0)中具有某种“1”作为字形,然后下一个插槽(位置 1)具有某种“4”,然后下一个插槽(位置 2)具有不同的类似于“1”等等——我们根据输出中想要的内容定义了字符。
从.mf
文件开始,您可以先在其上运行 Metafont(使用正确的参数,我现在无法弄清楚)来生成字形和字体指标(文件),然后运行使用它的.tfm
以下文件:.tex
\font\postalfont=postal
$\pi$ is roughly three point {\postalfont \char0 \char1 \char2 \char3 \char4 \char5 \char6 \char7 \char8 \char9 \char10 \char11 \char12 \char13 \char14} in value.
\bye
在这个.tex
文件中,我们手动写出了“\char0 \char1...”等以使其更明确,但当然 TeX 有宏可以使其更容易。不仅如此,如果找不到字体,现代 TeX 发行版将自动调用 Metafont。此外,TeX 可以写入文件。使用所有这些,我们可以得到以下内容。
自动化单文件解决方案
删除之前剩余的所有内容,并创建一个.tex
包含以下内容的文件:
\newwrite\fontfile
\immediate\openout\fontfile=postal.mf
\catcode`\^^M=\active \def^^M{^^J} \catcode`\#=12 \immediate\write\fontfile{
leftgap := 0.1;
rightgap := 0.1;
penthickness := 0.08;
midlineheight := 0.5;
toplineheight := 1;
width# := 9pt#;
height# := 10pt#;
scale := 1;
mode_setup;
def begincchar(expr c) =
beginchar(c, width# * scale, height# * scale, 0);
t := toplineheight * h;
m := midlineheight * h;
a := leftgap * w; b := w - rightgap * w;
z1=(a,t); z2=(b,t);
z3=(a,m); z4=(b,m);
z5=(a,0); z6=(b,0);
pickup pencircle scaled (penthickness * w / scale);
penlabels(1,2,3,4,5,6);
enddef;
def drawzero = draw z2--z1--z5--z6--cycle; endchar; enddef;
def drawone = draw z3--z2--z6; endchar; enddef;
def drawtwo = draw z1--z2--z4--z5--z6; endchar; enddef;
def drawthree = draw z1--z2--z3--z4--z5; endchar; enddef;
def drawfour = draw z1--z3--z4; draw z2--z6; endchar; enddef;
def drawfive = draw z2--z1--z3--z4--z6--z5; endchar; enddef;
def drawsix = draw z2--z3--z5--z6--z4--z3; endchar; enddef;
def drawseven = draw z1--z2--z3--z5; endchar; enddef;
def draweight = draw z2--z1--z5--z6--cycle; draw z3--z4; endchar; enddef;
def drawnine = draw z4--z3--z1--z2--z4--z5; endchar; enddef;
} \catcode`\^^M=5 \catcode`\#=6
\newcount\n
\n=0
\newcount\m
\m=0
\def\name#1{%
\ifcase#1
zero
\or one%
\or two%
\or three%
\or four%
\or five%
\or six%
\or seven%
\or eight%
\or nine%
\fi
}
\def\addchar#1{%
\immediate\write\fontfile{
begincchar(\number\n); "#1"; scale := scale*0.95; draw\name#1;}%
\advance\n by 1}
% Add the next character as char \n to the current font, and repeat
% When \relax is seen, define \postalfont and print chars 0 to \n from it.
\def\vanishing#1{%
\ifx#1\relax
\immediate\write\fontfile{end}%
\immediate\closeout\fontfile
\font\postalfont=postal
\postalfont
\loop \char\m \advance\m by 1 \ifnum\m<\n \repeat
\tenrm
\ \else%
\addchar#1%
\expandafter\vanishing\fi}
$\pi$ is roughly three point \vanishing 141592653589793\relax in value.
\bye
最后两行之前的所有内容都是设置,它定义了一些宏并从 TeX 本身写出文件的初始部分.mf
。最后两行是实际用法:您可以从 TeX 写入\vanishing 141592653589793\relax
并“自动”获取根据不同参数生成的相应字符。输出与之前相同:
(当然,这只是概念验证,在各种条件下都会失败,而且我对字体设计一无所知,而且还有更好的方法可以做到这一点,但我认为这可以给你一些想法。)