从以下 TeX 文件获得的 DVI 文件是好的:
\font\eightrm=cmr8
\let\mainfont=\tenrm
\newtoks\gtitle % title of current major group
\newif\iftitle \newif\ifpagesaved
\newif\ifheader
\def\lheader{\headertrue\mainfont\the\pageno\eightrm\qquad\grouptitle
\hfill\title\qquad\mainfont\topsecno} % top line on left-hand pages
\def\rheader{\headertrue\mainfont\topsecno\eightrm\qquad\title\hfill
\grouptitle\qquad\mainfont\the\pageno} % top line on right-hand pages
\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter
\takethree\topmark}}
\def\topsecno{\expandafter\takeone\topmark}
\def\takeone#1#2#3{#1}
\def\takethree#1#2#3{#3}
\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers
\let\page=\pagebody \raggedbottom
% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes
\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
\shipout\vbox{
\vbox to\fullpageheight{
\iftitle\global\titlefalse
\else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi
\vfill#1}} % parameter #1 is the page itself
\global\advance\pageno by1}
\gtitle={\.{CWEB} output} % this running head is reset by starred sections
\mark{\noexpand\nullsec0{\the\gtitle}}
\def\title{\expandafter\uppercase\expandafter{\jobname}}
\newdimen\pagewidth \pagewidth=6.5in % the width of each page
\newdimen\pageheight \pageheight=8.7in % the height of each page
\newdimen\fullpageheight \fullpageheight=9in % page height including headlines
\newdimen\pageshift \pageshift=\hoffset % shift righthand pages wrt lefthand ones
\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size
\def\contentsfile{\jobname.toc} % file that gets table of contents info
\newwrite\cont
\output{\setbox0=\page % the first page is garbage
\openout\cont=\contentsfile
\global\output{\normaloutput\page\lheader\rheader}}
\setpage
\vbox to \vsize{} % the first \topmark won't be null
\newbox\sbox % saved box preceding the index
\newbox\lbox % lefthand column in the index
% it seems that if there is only one line at the top, the output routine above is
% not executed:
%abc
abc\par ok
\par\vskip6pt plus 1fil % we are beginning the index
\def\page{\box255 } \normalbottom
\write\cont{If you see this on stdout, the output routine above was not executed}
\output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
\global\setbox\sbox=\page \global\pagesavedtrue}
\pagesavedfalse \eject % eject the page-so-far and predecessors
\setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
\vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
\hsize=.5\pagewidth \advance\hsize by -10pt
% column width for the index (20pt between cols)
\parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
\def\lr{L} % this tells whether the left or right column is next
\output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
\else\normaloutput{\vbox to\pageheight{\box\sbox\vss
\hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
\global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
\parskip 0pt plus .5pt
\rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
\hyphenpenalty 10000 \parindent0pt
hello.
\par\vfill\eject % this is done when we are ending the index
\ifpagesaved\null\vfill\eject\fi % output a null index column
\if L\lr\else\null\vfill\eject\fi % finish the current page
\end
但是如果我们取消注释第一个“abc”行并注释第二个,则输出将变为“坏”,因为它输出在两页而不是一页上。
为什么对 TeX 文件进行这么小的修改会因为某种原因导致事情发生改变?
答案1
差异来自输出例程被调用的时间。当 TeX 将材料从最近贡献列表移动到当前页面时,就会执行该例程。
如果只abc
输入了 ,则当前页面由一个标记、一个 topskip 和一个空的 vbox 组成;带有 的段落abc
是最近的贡献。当 TeX
\eject
在几行之后看到 时,将调用输出例程,并且输出具有足够材料的当前页面,因为此时处于活动状态的输出例程不再是指出第一页是垃圾的例程。在文件末尾,第二页由当时处于活动状态的输出例程输出。
在第二种情况下,包含 的段落的开头ok
调用输出例程,而最近的贡献,即带有 的段落
abc
,将成为当前页面。输出例程会丢弃空的 vbox。随后,新的输出例程会保存这两个段落,并由第三个输出例程输出。
你可以使用命令查看当前页面和最近的贡献\showlists
。例如,更改你的代码:
% it seems that if there is only one line at the top, the output routine above is
% not executed:
\showlists
abc
%abc\showlists\par ok
\showlists
并尝试这两行。要查看调用了哪个输出例程,请\tracingall
在文件开头添加 并搜索\output->
。您会看到,在 % 行中,总数有时大于目标,但这是正常的,因为 TeX 通常会收集比必要更多的材料来找到一个好的分页符。
答案2
abc\par OK
使用时,您正在处理主垂直列表中的以下项目:
- set first special output routine (line 42)
- put \vbox to\vsize (line 46), this makes the page full, but cost is zero
- put one-line paragraph "abc", this makes the page overfull, cost is infinity
- put second paragraph "OK":
when the \parskip of this paragraph is processed, then pageberak is found
after \vbox to\vsize, then first special output is invoked which throws away
this \vbox because \shipout (or saving \box255) is missing here
- the main vertical list keeps "abc" and "OK" lines.
- set second special \output (line 61 of your obscure code)
- put another vertical material following by \end
this invokes second special output, page [1].
abc
并且仅在使用时才处理主垂直列表中的以下项目:
- set first special output routine (line 42)
- put \vbox to\vsize (line 46), this makes the page full, but cost is zero
- put one-line paragraph "abc", this makes the page overfull, cost is infinity
- set second special \output (line 61 of your obscure code)
- put another material, when the next \parskip is put then:
- the page breaking point is set after \vbox to\vsize
- the second special output prints this empty \vbox, page [1]
- the line "abc" is kept in main vertical list
- put another vertical material following by \end
this invokes secondly the second special output, page [2]
因此,这就是我对您的晦涩代码如何工作以及这种奇怪行为的原因的解释。我强烈建议删除所有此类代码并从头开始编写更优雅的代码。