我在我的 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{
被执行。
“裸” 发货操作正确执行。更改输出例程也表明这可能在所有正常情况下都能正常工作。
更好的方法可能是使用atbegshi
Heiko Oberdiek 提供的包,而不需要重新发明轮子。