PGF/TikZ 和 Plain TeX 输出程序的奇怪行为

PGF/TikZ 和 Plain TeX 输出程序的奇怪行为

我在我的 Debian 机器上运行带有 pdfTeX 的 PGF/tikZ 时注意到一个奇怪的行为:一个没有 tikz 的简单会话:

pdftex
**\relax
*Hello World
*\bye

在页脚上输出页码。另一方面,加载 tikz:

pdftex
**\relax
*\input tikz.tex
*Hello World
*\bye`

确实会使页脚上的页码消失。页面大小似乎不是原因。

你们有人知道这种行为背后的原因吗?

答案1

问题是如何pgf处理\shipout。它将其重新定义为

\afterassignment\pgfutil@@EveryShipout@Test\setbox 255=

如果在普通调用时设置了框 255 的内容,那就没问题了\shipout。然而,事实并非如此:

\plainoutput ->\shipout \vbox {\makeheadline \pagebody \makefootline }\advancepageno    
  \ifnum \outputpenalty >-\@MM \else \dosupereject \fi

\makefootline特别注意这里。出错的地方是\afterassignment插入 token\pgfutil@@EveryShipout@Test不在结束 }但之后开幕 {括号,IE就在之前\makeheadline。(这种行为在常见的地方有记录,例如TeX 按主题分类

这都是一个问题,因为在作为 扩展的一部分pgf触发原语之前,then 对框 255 进行了一些测试。因此,发货发生在添加头/脚线之前:坏消息。(您可以在输出中看到这一点,框出现在完全错误的位置。)\shipout\pgfutil@@EveryShipout@Test\tracingall

答案2

您可以检测到插入的标记\afterasignment是在新vbox列表中而不是当前列表中,如果是这样,则使用 延迟它们\aftergroup。两个版本,一个用于经典 TeX,然后是\bye的版本etex

\input tikz.tex
\catcode`\@=11
\def\shipout{\vskip1sp \afterassignment \zzz \setbox 255= }

\def\zzz{%
\ifdim\lastskip=\z@\expandafter\aftergroup\fi
\zzzz}

\def\zzzz{%
\unskip
\pgfutil@@EveryShipout@Test}

Hello World
\bye


\input tikz.tex
\catcode`\@=11
\def\shipout{\afterassignment \zzz \setbox 255= }

\def\zzz{%
\ifnum\currentgrouptype=4 \expandafter\aftergroup\fi
\pgfutil@@EveryShipout@Test }

Hello World
\bye

答案3

问题在于,正如 Joseph Wright 所发现的(比我早了一分钟),PGF 对如何\shipout调用操作做出了一些错误的假设。如果像在 LaTeX 中一样,

\shipout\box255

(或其他盒子寄存器,这无关紧要),重新定义\shipout就可以了。但重新定义是

\def\shipout{\afterassignment\pgfutil@@EveryShipout@Test\setbox255= }

\shipout\vbox{\makeheadline\pagecontents\makefootline}当由 Plain TeX 的输出例程调用时,就会出错。事实上,TeXbook 说

\afterassignment<token><token>保存在一个特殊位置;它将在执行下一个赋值命令后立即插入回输入中。赋值不必紧随其后;如果在下一个\afterassignment赋值之前执行了另一个赋值,则第二个赋值将覆盖第一个赋值。如果下一个赋值是\setbox,并且赋值的<box>\hbox\vbox\vtop,则<token>将在框构造之后插入{,而不是 之后;它还将出现在或}插入的任何标记之前。\everyhbox\everyvbox

因此,测试是在错误的时间执行的,在\makeheadline\makefootline执行之前,所以不会出现页眉和页脚。

PGF 还做出了另一个错误的假设,即人们总是\shipout\box<register>将盒子称为\vbox。因此,一个简单的

\setbox0=\hbox{A}
\shipout\box0

会失败(Incompatible list can't be unboxed)。这是一个可能的解决方法(使用 David Carlisle 的答案中的想法)。

\input pgf

\catcode`@=11
\newbox\pgfutil@@Output@Box
\def\shipout{%
  \ifhmode\hskip\else\vskip\fi 1sp 
  \afterassignment\pgfutil@@Delayed@EveryShipout@Test
  \setbox\pgfutil@@Output@Box=}
\def\pgfutil@@Delayed@EveryShipout@Test{%
  \ifdim\lastskip=\z@ % it was \shipout\vbox or \shipout\hbox
    \expandafter\aftergroup
  \fi
  \pgfutil@@EveryShipout@Test}
\def\pgfutil@@EveryShipout@Test{%
  \unskip % remove the skip used as signal
  \ifvoid\pgfutil@@Output@Box
    \expandafter\aftergroup
  \fi
  \pgfutil@EveryShipout@Output}
\def\pgfutil@EveryShipout@Output{%
  \setbox\pgfutil@@Output@Box=\vbox{
    \setbox\z@=\hbox{%
      \pgfutil@abe
      \unhbox\pgfutil@abb
      \pgfutil@abc
      \global\let\pgfutil@abc\pgfutil@empty
    }%
    \wd\z@=\z@\ht\z@=\z@\dp\z@=\z@\box\z@
    \ifhbox\pgfutil@@Output@Box\unhbox\else\unvbox\fi\pgfutil@@Output@Box
  }%
  \pgfutil@@EveryShipout@Org@Shipout\box\pgfutil@@Output@Box
}
\catcode`@=12

%\def\bxxx{\box255}\output={\shipout\bxxx}
%\output={\shipout\relax\box255}

\setbox0=\hbox{A}
\shipout\box0

Hello world
\bye

如果出现这种情况,可能会产生错误的结果\shipout\vtop{...},但这似乎不是经常发生的事情。我们是否属于一个\vtop组,使用 e-TeX 的测试\ifnum\currentgrouptype=5可以在条件中进行\ifdim\lastskip=\z@,稍后在

\setbox\pgfutil@@Output@Box=\vbox{

被执行。

“裸” 发货操作正确执行。更改输出例程也表明这可能在所有正常情况下都能正常工作。

更好的方法可能是使用atbegshiHeiko Oberdiek 提供的包,而不需要重新发明轮子。

相关内容