14

Suppose I want to define an environment which is essentially a modification of an existing one. I tried something like that:

\newenvironment{newenv}{%
  \formatcommand{\begin{oldenv}%
}{%
  \end{oldenv}}%
}

I would expect

\begin{newenv}Foo\end{newenv}

to result in the same output as

\formatcommand{\begin{oldenv}Foo\end{oldenv}}

But whenever I use \begin{newenv}, pdflatex says

Missing } inserted

and skrews up everything afterwards, of course. Without \formatcommand everything is fine, so apparently the orphan { form \formatcommand is the culprit.

My guess is that pdflatex tries to treat stuff inside environment definition parts as something syntactically correct---which what I do clearly is not---instead of "just" to replace text and parse afterwards.

Is there a way to get around this, i.e. have a command span from a new environments beginning to end?

Use case:

\newenvironment{card}{%
  \resizebox{55mm}{85mm}{\begin{tabular*}{55mm}{| p{50.5mm} |}%
  \hline%
}{%
  \hline\end{tabular*}}%
}

If I put the same resizebox around the usage site of card, it compiles and resizes (even though not the way I intended, but that's for another day).

4
  • Not sure what you are tying to do here. Why are you passing the \begin{oldenv} to \formatcommand? Commented Sep 28, 2011 at 20:56
  • 1
    It mostly depends on what \formatcommand is supposed to do and on what oldenv is. A "real world" example is needed.
    – egreg
    Commented Sep 28, 2011 at 20:57
  • I would have thought that this issue relates to general properties of (La)TeX, but there you go.
    – Raphael
    Commented Sep 28, 2011 at 21:07
  • @PeterGrill I want to have \formatcommand applied to the whole environment oldenv. As I repeat card often in the application and need the same resize, I figured an environment def would be the place to put it.
    – Raphael
    Commented Sep 28, 2011 at 21:12

5 Answers 5

12

For applying some macro/command to the body of an environment, you may want to extract the entire body into a macro itself. This can be done (to whatever limited extent) using the environ package. Here is a short example:

\documentclass{article}
\usepackage{environ}% http://ctan.org/pkg/environ
\NewEnviron{itquote}{%
  \itshape% Set shape to italics
  \begin{quote}
    \BODY% regular \BODY of itquote environment
  \end{quote}
}
\begin{document}
\begin{quote}% Original quote environment
  Here is a very simple quote
\end{quote}
\begin{itquote}% New itquote environment
  Here is a very simple quote
\end{itquote}
\end{document}
​

enter image description here

In the above (admittedly elementary) example, itquote italicizes its contents (stored in the macro \BODY). For some, it provides a more intuitive way of working with environments within environments.

Additionally, it is always good to view the package documentation (and sometimes even the package source .sty). For example, although minipage is an environment and is typically used in the context of \begin{minipage}{<width>} ... \end{minipage} it can also be used in a "macro pair form" using \minipage{<width} ... \endminipage. Again, for some, this allows for a more intuitive way of splitting environment begin/end's over a new environment definition.

1
  • That works wonderfully, thanks! Good hint regarding macro pairs, too, even though I don't see myself digging is sty files.
    – Raphael
    Commented Sep 28, 2011 at 21:25
6

You can't leave unbalanced braces in a definition. That's forbidden by TeX's syntax rules.

There is a workaround:

\newsavebox{\cardbox}
\newenvironment{card}
  {\begin{lrbox}{\cardbox}
   \begin{tabular*}{55mm}{| p{50.5mm} |}
   \hline
  }
  {\hline
   \end{tabular}
   \end{lrbox}%
   \resizebox{55mm}{85mm}{\usebox{\cardbox}}%
  }

The contents of the environment is saved in the \cardbox bin which is later processed.

See Werner's answer for another method which is particularly useful in other situations.

3
  • I don't understand what happens here, and it seems a lot of boilerplate compared to environ. Does your solution have technical advantages?
    – Raphael
    Commented Sep 28, 2011 at 21:26
  • For example, environments defined with environ can't go in the argument of commands, nor in the definition of other environments. The lrbox way uses only kernel methods (except \resizebox, of course).
    – egreg
    Commented Sep 28, 2011 at 21:31
  • I see. In my case, the environment in queston is going to be top-level so I'll stick with environ, but I appreciate your answer.
    – Raphael
    Commented Sep 28, 2011 at 21:34
4

Here is an example of defining a new environment:

\documentclass{article}
\usepackage{lipsum}
\newenvironment{MyQuote}{%
    \begin{quote}%
    % Add customization that goes after the start of the environment here
}{%
    % Add customization that goes before the end of the environment here
    \end{quote}%
}%

\begin{document}
  \begin{MyQuote}
    \lipsum[1]
  \end{MyQuote}
\end{document}

I suspect that you are trying to use some macro of the form \macro{} and attempting to add the \macro{ at the beginning and } at the end environment. Don't think this is allowed with the standard \newenvironment. You could use the environ package, with which provides access to the body of the environment via \BODY. Here is an example:

\documentclass{article}
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{environ}

\NewEnviron{MyQuote}{%
  \quote%
  \textcolor{red}{\BODY}%
}{%
  \endquote%
}
\begin{document}
  \begin{MyQuote}
    \lipsum[1]
  \end{MyQuote}
\end{document}
2
  • This answer does not relate to the question. I tried to clarify that the general way of defining new environments is not the issue (or so I think) but specifically how to have a command span from before its begin to after its end.
    – Raphael
    Commented Sep 28, 2011 at 21:03
  • See updated regarding the environ package. Commented Sep 28, 2011 at 21:13
0

What should \formatcommand do?

Your definition has no balanced braces, I hope the following sequence shows, what happens:

\newenvironment{newenv}{%Open parameter 2 of \newenvironment
  \formatcommand{\begin{oldenv}%
}%this closes formatcommand
{%you open a group
  \end{oldenv}
}%The group is closed%
}%you close parameter 2 of \newenvironment
%%Now you should get the 3rd parameter of \newenvironment
1
  • This clearly is what happens and I stated as much in the question. Do you have a solution?
    – Raphael
    Commented Sep 28, 2011 at 21:09
0

Update. I'll leave it here as a non-answer, see comments below.


It looks like \bgroup and \egroup can help. I am not an expert and not sure if this is the right way to do, but the following works:

\documentclass{article}
\newenvironment{oldenv}{*}{*}
\newenvironment{newenv}{\textit\bgroup\begin{oldenv}}{\end{oldenv}\egroup}
\begin{document}
  \begin{newenv}
    Test
  \end{newenv}
\end{document}
3
  • 1
    Just by chance. You're passing \bgroup as the argument to \textit and the business seems to work, but really by chance.
    – egreg
    Commented Apr 1, 2020 at 9:58
  • @egreg, is there no way to make something like this with \begingroup or \bgroup?
    – Alexey
    Commented Apr 1, 2020 at 10:02
  • 1
    Not really. The syntax rules of TeX are that an (undelimited) argument is either the next token or, if the next token is { (not \bgroup), the whole token list up to the matching }, which TeX absorbs immediately without interpreting anything.
    – egreg
    Commented Apr 1, 2020 at 10:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .