测试 \end 是否真的会结束运行

测试 \end 是否真的会结束运行

我可以测试调用\end原语是否真的会结束运行吗?

单纯\ifdim\pagegoal=16383.99998pt yes\else no\fi是不够的(甚至还加上了这种混合\deadcycles)。根据 Victor Eijkhout 的TeX 按主题分类,第 27.2 节:

\end命令 --- 只允许在外部垂直模式下使用 --- 终止 TeX 作业,但前提是主垂直列表为空且\deadcycles=0

不幸的是,\pagegoal即使主垂直列表非空,也可能是 16383.99998pt:以下产生一页输出。

\write16{}
\message{\the\pagegoal}
\end

可能还有其他方法可以完成我想做的事情。上下文是,我可以完全控制发送到 TeX 的每个标记(在 TeX 中解析 TeX --- 不要告诉我我不应该这样做),并且我需要在输出最后一页后</body></html>以可靠的方式执行某些操作(即写入文件)。在\end感知到原语时执行此操作可能还不够晚。

答案1

是的,我们可以!:-)(假设我正确理解了这个问题)

但只能以迂回的方式,并且只有当您完全控制调用的输出例程时才可以。基本上,要\end结束 TeX 作业,需要主垂直列表为空且\deadcycles值为 0。

现在,除非您跟踪 TeX 内部管理的所有内容,否则无法从主文档中确定是否\end会结束运行。您可以查看,\pagegoal但所有信息都告诉您当前页面是否包含任何框,但这并不考虑其他节点,例如“写入”和我认为的“特殊”。

但在相反的方向上,你可以确保页面在\end执行时不为空,例如,尽管像这样的序列

\endgraf\vbox{}\end

这样您肯定会得到一个输出例程调用,然后您可以检查这是由 触发\end\outputpenalty-1073741824此时,您将在框 255 中拥有所有材料,然后是您的 vbox。

现在,这没有考虑到插入或脚注的遗留问题,这些遗留问题可能会被拆分。如果这是一个问题,那么可以使用更复杂的方案,例如 LaTeX 使用的方案\clearpage。基本算法如下:

  • 结束当前页面(通过\newpage
  • 随后\vbox{}\penalty -10001表示我们要执行清除页面操作
  • 然后 OR 输出浮点数并查看脚注插入
  • 并反复发出进一步的\clearpage命令,直到所有浮点数和注记都排版完毕
  • 只有这样,这个循环才会结束。

(由于可能有两列复杂性,整个序列会稍微复杂一些,但是......)

因此,LaTeX 实际所做的就是或多或少地运行,\clearpage\end这意味着当\end遇到时,所有应该排版的内容都已经排版好了。

Plain TeX 的工作原理类似,但使用输出例程来\insertpenalties > 0决定是否需要循环。这在 LaTeX 中是不可能的,因为浮点数不是真正的插入,而基本上只是用作框。

答案2

感谢弗兰克的建议,我解决了我的问题。

的任务\theend是执行\end原语要做的事情(调用输出例程,直到页面为空且 \deadcycles为零),然后打印结束,通过调用原始的立即结束\end

主要思想是临时重新定义\output并强制调用它,这样我就可以检查当前页面中的内容。如果它是空的,我就真的完成了,我可以安全地关闭我的 html 文件。否则,我将材料放回页面中,并确保在使用要插入的\output内容强制调用它之前恢复原始内容\end,即\hbox{}\vfill\penalty-1073741824。为了使其更强大,解决方案应该添加测试以测试我们处于哪种模式,\par例如添加水平模式。

% Helpers:
%
\long\def\T#1#2{#1}
\long\def\F#1#2{#2}
%
% Firstly, the output routine should be called if either
% |\deadcycles| is non-zero or |\pagegoal| is not |\maxdimen|
% (meaning that there is a something in the main vertical list).
% This is acheived by inserting what |\end| would insert
% (see \TEforceoutput), then calling |\theend| again.
%
\def\theend
  {%
    \ifdim\pagegoal=\maxdimen
      \ifnum 0=\deadcycles
        \expandafter\expandafter\expandafter\T
      \else \expandafter\expandafter\expandafter\F
      \fi
    \else \expandafter\F
    \fi
    {\TEtest}%
    {\TEforceoutput\theend}%
  }
\def\TEforceoutput{\hbox{}\vfill\penalty-1073741824 }
%
% If both \pagegoal=\maxdimen and \deadcycles=0, there is no box
% in the page, but we still need to test whether the page was really
% empty. For that, set the \output routine to a test, and force a call
% to it using |\TEforceoutput|.
%
% The test resets |\deadcycles| (because we may call the true |\output|
% routine afterwards), then removes from |\box255| the spurious material
% which we had added to force a call to \output. If the resulting page
% is empty, then we reached the true end of the run. Otherwise, after
% the test |\output| routine has ended, we must force the true |\output|
% to take place, then go all the way back to |\theend|.
%
% Of course, don't forget to place the page's contents back into the main
% vertical list, so that potential |\special| and |\write| are not lost.
%
\def\TEtest
  {%
    \begingroup\output{\aftergroup\endgroup\TEoutput}%
    \TEforceoutput
  }
\def\TEoutput
  {%
    \deadcycles=0
    \TEcleanpage
    \TEifpageempty
      {\immediate\write16{** The end! **}\aftergroup\end}%
      {\aftergroup\TEforceoutput\aftergroup\theend}%
    \unvbox255
  }
%
% How to ``clean'' the page from the material that was added to
% force an |\output|: remove the skips and boxes.
%
\def\TEcleanpage
  {%
    \setbox255\vbox
      {%
        \unvbox255
        \unskip % remove \vfill
        \setbox0\lastbox % remove empty \hbox{}
        \unskip % remove \topskip
      }%
  }
%
% We have tested that the page contains no box. If it contains
% other material, then the last item in |\vbox{}\unvcopy255|
% is not a box, hence |\lastbox| is void.
%
\def\TEifpageempty%
  {%
    \setbox0\vbox
      {%
        \vbox{}\unvcopy255\setbox0\lastbox
        \expandafter
      }%
    \ifvoid0 \expandafter\F\else\expandafter\T\fi
  }
%
% Try un-commenting the next line.
\write16{** Hi **}
\theend

相关内容