Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am aware that it is not generally possible to modify the contents of a box (as per http://tex.stackexchange.com/a/49903/17427), though I believe it is sometimes possible to "unbox" material and hack away at it (but not familiar with that).

However, my interest at the moment lies in removing (or disabling) a \write node from a box, which would not affect how it appears etc. The motivation for this is primarily as per my "answer" here regarding the way beamer puts a shrunk copy of slides in the notes pages and the resulting multiply defined labels it causes. For the purposes of the MWE, suppose I am creating a box that contains a \label, but I want to show that box twice, once with the \label taking effect and later without, and I want to prevent the multiple labels issue without subverting other features of the labelling mechanism in general, and without collecting the raw code in the box and processing it twice.

I'm sure this is possible with LuaTeX, and I'm interested in solutions both with and without LuaTeX (hence I haven't tagged this as {luatex}).

MWE to play with:

\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}

The approach I've tried to take at the moment is not to delete the node from the box (as I have no idea where to start), but rather to add an extra layer of indirection to the \write before it goes in the box via \latelua, and then I should theoretically be able to change the behaviour of the indirection layer without editing the box. This may not be a good idea in general (thoughts?), but it shows I've made some effort! However, according to the manual, "the result of using tex.print from inside callbacks is undefined", and I am not sure if \latelua is considered a callback. In any case, the only things I've managed to do from a \latelua without my commands being silently ignored are tex.error and tex.sprint([[\noexpand\undefinedcs]]), and certainly nothing like tex.sprint([[\unexpanded{\immediate\write\@auxout{...}}]]).

Lua indirection attempt (but note my label isn't written to the aux file before I disable writing):

\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}

(Note also that this is my first real attempt to do something in Lua!)

share|improve this question
1  
One other idea would be for my \latelua node to replace itself with a real \write node? But again, I'm stuck as to how one might go about that. –  cyberSingularity Nov 11 '12 at 0:56
1  
If you just want to get rid of the write whatsit, outputting the box with \leaders ignores write whatsits. –  Stephan Lehmke Nov 11 '12 at 5:14
2  
Well for instance, my TeXPower package, which also outputs boxes multiple times, uses \global\setbox\@cclv=\hbox{\leaders\copy\@cclv\hskip\wd\@cclv} to "filter out" whatsits before shipout. –  Stephan Lehmke Nov 11 '12 at 10:07
1  
So, \setbox\boxb=\hbox{\leaders\copy\boxa\hskip\wd\boxa} would be a possible solution. –  Stephan Lehmke Nov 11 '12 at 10:20
    
@StephanLehmke: Thanks very much. I have used this suggestion in a patch to beamer in the linked question. Please post your comment as an answer and I will accept it (though other answers regarding Lua will still be of interest!) –  cyberSingularity Nov 11 '12 at 12:53
show 1 more comment

2 Answers

up vote 6 down vote accepted

If you just want to get rid of the write whatsit, outputting the box with \leaders ignores write whatsits.

So, \setbox\boxb=\hbox{\leaders\copy\boxa\hskip\wd\boxa} would be a possible solution.

share|improve this answer
add comment

You can not, in classic TeX, remove a whatsit node, however if you have control over the macro layer and can control the construction of the original box you can arrange that the whatsit never appears in the top level list and is always protected by a box that you can remove. This is one of the main reasons why LaTeX3 xgalley code needs to take full control of all vertical commands, so that it can adjust vertical lists afterwards.

In this case you can do

\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}

Producing

> \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
share|improve this answer
    
Thanks, it works for my (old) MWE, but what if I don't know that the \label or \write is the last item in the box? Your unbox/rebox trick doesn't appear to work in that situation. –  cyberSingularity Nov 11 '12 at 11:52
    
You loop through the box copying nodes that want copying and not ones that don't want copying. This latter can be interesting as you have to recognise the box. You could for example reject all zero sized boxes but that might be too agressive or you could make your guard box have a recognisable size say depth -1sp and reject all such boxes or... –  David Carlisle Nov 11 '12 at 11:54
    
well actually it's a bit harder in hmode as in your example as there are more non-removable item types notably characters that you would have to hide. –  David Carlisle Nov 11 '12 at 12:05
    
Thanks, so you're suggesting that a pure TeX solution is probably too difficult. What do you think of Stephan's idea in the comments about using \leaders? I have got it working as a patch for beamer but not sure if you think it's a good idea? –  cyberSingularity Nov 11 '12 at 12:11
    
Using \leaders is a brilliant suggestion for suppressing \write I told him in chat to make it an answer so he picks up some rep point votes:-) When I said "a bit harder" in the comment above it was English understatement it could be roughly translated into "you would have to redesign the whole of latex" so not impossible but... –  David Carlisle Nov 11 '12 at 12:18
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.