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?

5
  • 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. May 21 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 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. May 21 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 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. May 21 at 21:44

4 Answers 4

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.

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.

4
  • \global\sbox is not guaranted to work. It may accidentally but most latex assignments, lrbox, \newcommand, \setlength... can not be prefixed with \global May 21 at 21:04
  • So, what would you suggest, @DavidCarlisle, to globalize it instead? May 21 at 21:10
  • 1
    use \setbox the primitive, or an L3 global box ssignment) May 21 at 21:22
  • That is good to know, @DavidCarlisle. Thank you. May 21 at 21:45
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.

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.

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}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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