3

I'm attempting to fill a box, within an environment, to be used after the environment is closed, similar to how \begin{lrbox}{\boxname} ... \end{lrbox} works. However, for some reason it seems that after closing the environment the content of the box is gone.

\newsavebox{\somebox}

\newenvironment{myenv} { \begin{lrbox}{\somebox} }{ \end{lrbox} \usebox{\somebox} % This works }

\begin{myenv} Something \end{myenv}

\usebox{\somebox} % This doesn't

In the example above \usebox{\somebox} within myenv's closing block seems to work as expected, but not if used after \end{myenv}. Maybe there is some fundamental understanding of environments and/or boxes that I am missing, but I would expect what I am trying to do to be possible, since it's how the lrbox environment works?

Ramon
  • 133
  • 3
    The whole idea of environments is that their work occurs in their own group (as if the code were surrounded by {...}. Thus, by design, what happens in the environment is inaccessible to the outside code, unless the environment saves its content globally. – Steven B. Segletes May 21 '22 at 20:48
  • @StevenB.Segletes Right, I see. Indeed adding \global\sbox{\somebox}{\usebox{\somebox}} within the closing brace of myenv makes the content available after \end{myenv}. I guess the lrbox environment must be doing something similar to that? – Ramon May 21 '22 at 20:53
  • Yes, except that it is not lrbox that is "at fault", but the environment myenv which isolates the original lrbox operation from the main code. – Steven B. Segletes May 21 '22 at 20:55
  • Right, makes sense. I guess I got confused expecting the lrbox behavior to be the default. I think I follow now, thanks! – Ramon May 21 '22 at 20:58
  • the definition of lrbox is uniquely coded by hand avoiding all the normal environment forms, so that it works as you describe. – David Carlisle May 21 '22 at 21:44

4 Answers4

4

The whole idea of environments is that their work occurs in their own group (as if the code were surrounded by {...}. Thus, by design, what happens in the environment is inaccessible to the outside code, unless the environment saves its content globally.

As the OP concluded from my comment, the content can be made accessible by globally saving the content of \somebox before leaving the environment. David recommends using a \setbox, rather than \sbox, as a reliable way to make the assignment global:

\documentclass{article}
\newsavebox{\somebox}

\newenvironment{myenv} { \begin{lrbox}{\somebox} }{ \end{lrbox} \usebox{\somebox} % This works \global\setbox\somebox=\hbox{\usebox{\somebox}}% }

\begin{document} \begin{myenv} Something \end{myenv}

\usebox{\somebox} % This now works with \somebox globally saved

\end{document}

The issue is not that the lrbox prevents its work from re-use, but that the closing of myenv effectively isolates the lrbox from the main code.

2

Boxes are assigned locally by LaTeX, so after the end of myenv the assignment is lost.

\newsavebox{\somebox}% for global assignment
\newsavebox{\someboxinternal}% for local assignment
\newenvironment{myenv}
 {%
  \begin{lrbox}{\someboxinternal}%
 }
 {%
  \end{lrbox}%
  \global\setbox\somebox=\hbox{\unhbox\someboxinternal}%
  %\usebox{\somebox}% <-- if you want to use it inside myenv
 }

What's the difference between this and Steven's answer?

  1. the box register \somebox is only assigned globally: local and global assignments to the same register should be avoided
  2. there is no added level of boxing like in \global\setbox\somebox=\hbox{\usebox{\somebox}}

If I run

\showboxbreadth=1000 \showboxdepth=1000
\tracingonline=1
\documentclass{article}

\newsavebox{\somebox}% for global assignment \newsavebox{\someboxinternal}% for local assignment \newenvironment{myenv} {% \begin{lrbox}{\someboxinternal}% } {% \end{lrbox}% \global\setbox\somebox=\hbox{\unhbox\someboxinternal}% %\usebox{\somebox}% <-- if you want to use it inside myenv }

\begin{document}

\begin{myenv} abc \end{myenv}

\showbox\somebox

\stop

the console shows

> \box50=
\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

If I replace the global assignment with

\global\setbox\somebox=\hbox{\usebox\someboxinternal}%

the console shows

> \box50=
\hbox(6.94444+0.0)x15.27782
.\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

and you see the nesting.

egreg
  • 1,121,712
2

The reason of the problem (LaTeX environment creates a group) was mentioned in another answers. You can use only TeX primitives, it gives you much more control what is done:

\newbox\somebox

\def\myenv{\global\setbox\somebox\hbox\bgroup\bgroup\ignorespaces} \def\endmyenv{\unskip\egroup\egroup}

\begin{myenv} Something \end{myenv}

\box\somebox % This does

The double \bgroup and \egoup is here because of LaTeX's color management.

wipet
  • 74,238
1

I'm a fan of the environ package, so I will add my version based on Steven's one.

\documentclass{article}
\usepackage{environ}

\begin{document}

\newsavebox{\somebox}

\NewEnviron{myenv}{ \begin{lrbox}{\somebox} \BODY \end{lrbox} \usebox{\somebox} \global\setbox\somebox=\hbox{\usebox{\somebox}} }

Abc \begin{myenv} Something \end{myenv}

Def \usebox{\somebox}

If you only want to reuse stuff, you can go like this:

\NewEnviron{myenvII}{ \global\let\mycmd\BODY \mycmd }

\begin{myenvII} Another thing \end{myenvII}

\mycmd

\end{document}

MaestroGlanz
  • 2,348