![是否可以从盒子中移除(或以其他方式使之无效)某个 whatsit 节点?](https://linux22.com/image/258327/%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E4%BB%8E%E7%9B%92%E5%AD%90%E4%B8%AD%E7%A7%BB%E9%99%A4%EF%BC%88%E6%88%96%E4%BB%A5%E5%85%B6%E4%BB%96%E6%96%B9%E5%BC%8F%E4%BD%BF%E4%B9%8B%E6%97%A0%E6%95%88%EF%BC%89%E6%9F%90%E4%B8%AA%20whatsit%20%E8%8A%82%E7%82%B9%EF%BC%9F.png)
我知道一般来说不可能修改盒子里的内容(根据https://tex.stackexchange.com/a/49903/17427),尽管我相信有时可以“拆箱”材料并对其进行破解(但我对此并不熟悉)。
不过,我目前的兴趣在于\write
从框中删除(或禁用)节点,这不会影响它的外观等。这样做的动机主要是我的“答案”在这里关于将幻灯片的缩小副本放入注释页中的方式beamer
以及由此导致的多重定义标签。为了 MWE 的目的,假设我正在创建一个包含 的框\label
,但我想显示该框两次,一次显示\label
生效,一次不显示,并且我想防止出现多重标签问题,而不会破坏标签机制的其他功能,也不会收集框中的原始代码并对其进行两次处理。
我确信使用 LuaTeX 可以实现这一点,并且我对使用和不使用 LuaTeX 的解决方案都很感兴趣(因此我没有将其标记为 {luatex})。
与 MWE 一起玩:
\documentclass{article}
\tracingonline2
\showboxdepth\maxdimen
\showboxbreadth\maxdimen
\newbox\boxa
\newbox\boxb
\newcommand{\showtwo}[2]{\showtokens{^^J^^J#1, #2^^J^^J}}
\begin{document}
\section{Hello}
{
\setbox\boxa\hbox{abc\label{hi}def}
\showbox\boxa
\setbox\boxb=\copy\boxa
\box\boxa
\let\write\showtwo
% edit \boxb somehow
\showbox\boxb
\box\boxb
}
\end{document}
我目前尝试采用的方法不是从框中删除节点(因为我不知道从哪里开始),而是在节点\write
进入框之前通过添加一个额外的间接层\latelua
,然后理论上我应该能够在不编辑框的情况下更改间接层的行为。这可能不是一个好主意(想法?),但它表明我已经做出了一些努力!但是,根据手册,“从回调内部使用的结果未定义tex.print
”,我不确定是否\latelua
被视为回调。无论如何,我设法从 中执行的唯一操作(\latelua
而不会让我的命令被默默忽略)是tex.error
和tex.sprint([[\noexpand\undefinedcs]])
,当然没有像 这样的操作tex.sprint([[\unexpanded{\immediate\write\@auxout{...}}]])
。
Lua 间接尝试(但请注意,在我禁用写入之前,我的标签不会写入文件aux
):
\documentclass{article}
\tracingonline2
\showboxdepth\maxdimen
\showboxbreadth\maxdimen
\newbox\boxa
\newbox\boxb
\directlua{tex.enableprimitives('',tex.extraprimitives())}
\setcounter{errorcontextlines}{\maxdimen}
\usepackage{etoolbox}
\makeatletter
\patchcmd{\label}{\protected@write}{\protected@labelwrite}{}{\showtokens{patching \label failed}}
\let\protected@labelwrite\protected@write
\patchcmd{\protected@labelwrite}{\write#1}{\latelabelwritehelper{#1}}{}{\showtokens{patching \protected@labelwrite failed}}
\newcommand*{\latelabelwritehelper}[2]{%
\latelua{tex.sprint([[\noexpand\latelabelwrite{#1}{#2}]])}}
\newcommand*{\latelabelwrite}[2]{\immediate\write#1{#2}}
\newcommand*{\disablelatelabelwrite}{\let\latelabelwrite\@gobbletwo}
\makeatother
\begin{document}
\section{Hello}
\setbox\boxa\hbox{abc\label{hi}def}
\showbox\boxa
\setbox\boxb=\copy\boxa
\box\boxa
\disablelatelabelwrite
\showbox\boxb
\box\boxb
\end{document}
(还请注意,这是我第一次真正尝试用 Lua 做某事!)
答案1
如果您只是想摆脱写入的 whatsits,则输出带有\leaders
忽略写入 whatsits 的框。
所以,\setbox\boxb=\hbox{\leaders\copy\boxa\hskip\wd\boxa}
这可能是一个解决方案。
答案2
在经典 TeX 中,您无法删除 whatsit 节点,但是如果您可以控制宏层并可以控制原始框的构造,则可以安排 whatsit 永远不会出现在顶级列表中,并且始终受到可以删除的框的保护。这是 LaTeX3 xgalley 代码需要完全控制所有垂直命令的主要原因之一,以便它可以在之后调整垂直列表。
在这种情况下你可以
\documentclass{article}
\tracingonline2
\showboxdepth\maxdimen
\showboxbreadth\maxdimen
\let\oldlabel\label
\def\label#1{\hbox{\oldlabel{#1}}}
\newbox\boxa
\newbox\boxb
\newcommand{\showtwo}[2]{\showtokens{^^J^^J#1, #2^^J^^J}}
\begin{document}
\section{Hello}
{
\setbox\boxa\hbox{abc\label{hi}}
\showbox\boxa
\setbox\boxb=\copy\boxa
\box\boxa
\let\write\showtwo
% edit \boxb somehow
\setbox\boxb\hbox{\unhbox\boxb\setbox0\lastbox}
\showbox\boxb
\box\boxb
}
\end{document}
制作
> \box26=
\hbox(6.94444+0.0)x15.27782
.\OT1/cmr/m/n/10 a
.\OT1/cmr/m/n/10 b
.\kern0.27779
.\OT1/cmr/m/n/10 c
.\hbox(0.0+0.0)x0.0
..\write1{\newlabel{hi}{{1}{\thepage }}}
! OK.
l.17 \showbox\boxa
?
> \box27=
\hbox(6.94444+0.0)x15.27782
.\OT1/cmr/m/n/10 a
.\OT1/cmr/m/n/10 b
.\kern0.27779
.\OT1/cmr/m/n/10 c
! OK.
l.24 \showbox\boxb