TeX 和 Metafont 及其参数变化之间的关系

TeX 和 Metafont 及其参数变化之间的关系

“元字体的概念”Donald E. Knuth 以视觉方式探索了参数变化的可能性。但我有一些疑问:

  • 他是否使用 TeX 排版该文章?
  • TeX 是否允许连续改变参数?有时,即使在同一段落中,字母之间的变化也是可见的。在这种情况下,Metafont 和 TeX 之间的通信如何处理?

谢谢

答案1

Knuth 的元字体的概念抽象的PDF)不仅仅是一篇论文,更是一场表演,因为他利用每个段落本身的视觉外观来阐释其中的思想。在两个特定段落之间有一个单独的页面,其中包含诗篇 23 的设置,这也标志着论文本身从一种字体到另一种字体的过渡:

元字体的概念,诗篇 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并“自动”获取根据不同参数生成的相应字符。输出与之前相同:

自动的

(当然,这只是概念验证,在各种条件下都会失败,而且我对字体设计一无所知,而且还有更好的方法可以做到这一点,但我认为这可以给你一些想法。)

相关内容