31

A standard way to grab and store a short text (say, a title of a document) is to use something like

\def\title#1{\def\@title{#1}}.

But if the content to be stored is longer (say, an abstract}, I guess it is considered more elegant to specify it by something like

\begin{abstract}
  ...
\end{abstract}

on the user side.

So, the question is: what techniques are available to do something like this? (Some that spring to my mind immediately are: use a \vbox, use delimited arguments, use the environ LaTeX package, use ConTeXt \grabbufferdata (if I am not mistaken as to the name of that macro)...)

I'd be delighted to see all kinds of answers, for various engines (Knuthian vanilla TeX, eTeX, luaTeX) and formats (plain TeX, LaTeX2e, LaTeX3, ConTeXt).

2
  • 1
    To be honest, if you are planning to "grab" the environment content in a macro or token register, I'd advise to use the normal "control sequence" syntax. It looks less elegant in the document, but is more honest and less prone to provoke misunderstandings. An environment is supposed to have the semantics that something is executed at the beginning, then the content is executed and then something is executed at the end. Changing that will confuse especially new users, because they can't understand why they can't use \verb inside or whatever. Sep 4, 2012 at 3:43
  • 2
    Good point. However, I can imagine at least two situations when this is not the case: (i) backward compatibility with existing classes/packages (the case of abstract) and (ii) situations when you want to do both: typeset and store some material for later reuse. And I wouldn't care too much about \verb, if you need to use it, you are probably writing about TeX itself, and that means that you're not a newbie;). (\url is a problem, though.)
    – mbork
    Sep 4, 2012 at 8:27

6 Answers 6

19

The collect package; a little example (defining an environment to collect text and format it between rules and using \section):

\documentclass{article}
\usepackage{collect}
\usepackage{lipsum}

\definecollection{test}
\makeatletter
\newenvironment{atest}[1]
  {\@nameuse{collect}{test}{\par\nobreak\noindent\hrulefill}
    {\par\nobreak\noindent\hrulefill\par\bigskip}
    {\section{#1}}
    {}%
  }
{\@nameuse{endcollect}}
\makeatother

\begin{document}

\begin{atest}{example one}
\lipsum[2-3]
\end{atest}

\begin{atest}{example two}
\lipsum[2]
\end{atest}

\lipsum[1-6]

\includecollection{test}

\end{document}

enter image description here

0
13

Straight LaTeX2e:

\newsavebox{\myabstractbox}
\newenvironment{myabstract}{\lrbox{\myabstractbox}}{\endlrbox}

Extract the contents with

\usebox{\myabstractbox}
2
  • 3
    This doesn't store the text, but only its printed representation.
    – egreg
    Sep 4, 2012 at 6:31
  • 2
    @egreg: yes, but sometimes this is perfectly fine.
    – mbork
    Sep 4, 2012 at 8:27
10

LaTeX

If you just want to store the text in form of tokens, like \title does, then you can do it with environ:

\usepackage{environ}
\NewEnviron{sabstract}{\global\let\@sabstract\BODY}

The environment's content is (locally) stored in the macro \BODY, so globally letting \@sabstract to it will do the same as \title does with \@title.

\begin{sabstract}
This is it.

With two paragraphs.
\end{sabstract}

Plain TeX

\catcode`@=11
\long\def\sabstract#1\endsabstract{\def\@sabstract{#1}}
\catcode`@=12

\sabstract
This is it.

With two paragraphs.
\endsabstract
9

In ConTeXt, \grabbufferdata is a low-level command that is only useful to module writers. At user level, buffers should be used to grab and store contents. For example, store the contents using

\startbuffer
    Whatever
\stopbuffer

and retreive them using:

\getbuffer

You may also typeset the contents using

\typebuffer

which is useful when you want to include verbatim material in tables and footnotes.

Buffers may be thought of as a clipboard for copy-pasting content; in fact, as named clipboard, which is useful when you want to repeat material in a document like a presentation or build metapost figures in steps. To get named buffers, simply store content using

\startbuffer[anything]
    This is a named buffer
\stopbuffer

and retrieve it using

\getbuffer[anything]

or

\typebuffer[anything]

Buffers are more robust than grabbing content using boxes because you do not need to care about catcodes while grabbing content. (Of course, you need to worry about them when retreiving content).

\startbuffer
  A C file starts with \type{#include<stdio.h>}.
\stopbuffer

\getbuffer

works fine even though the content has a #. For this reason, buffers are used when you want to process some XML inline in a regular TeX document.

Buffers can also be nested. For example:

\startbuffer[one]
   Buffer one
   \startbuffer[two]
      Buffer two
   \stopbuffer
\stopbuffer


\getbuffer[one]
\getbuffer[two]

(of course, buffer two is only available after buffer one has been retreived).

In MkIV, \startbuffer and \stopbuffer can be on the same line

\startbuffer[line] #line \stopbuffer
\typebuffer[line]

And finally, the contents of a buffer are available at the Lua end (as the content is stored in memory). Use the Lua function buffers.getcontent('name') to access the contents of the buffer named name.

Of course, like any other ConTeXt macro, you may define your own version of buffers.

\definebuffer
  [grabcontent]
  [
    before=...,
    after=...,
  ]

and then use them as regular buffers.

\startgrabcontent
  A # B C
\stopgrabcontent

\typegrabcontent

The before and after keys are used with \getgrabcontent.

Since buffers don't care about their content, they are an excellent comment environment. In fact, the hiding environment in ConTeXt is defined as a buffer.

\setupbuffer
  [\v!hiding]
  [\c!before=,
   \c!after=]
4
  • I've always wondered about the implementation here: I assume a toks holding material grabbed verbatim? (Perhaps that would make a good question!)
    – Joseph Wright
    Sep 4, 2012 at 6:49
  • @Aditya: thank you so much, I expected your comprehensive answer about ConTeXt;). As usual, I could learn from it...
    – mbork
    Sep 4, 2012 at 8:31
  • @JosephWright: I'm not sure, but I guess that it's some lua trickery, and I'm pretty sure that in MkII it was writing to a temporary file.
    – mbork
    Sep 4, 2012 at 8:32
  • Indeed, in MkII data was written to temp files. The implementation was similar to data grabbing macro from fancyverb package: data was parsed line by line and the grabbed string was compared with \stopbuffer. (That was the reason that \stopbuffer had to be on a line of its own). In MkIV, the implementation is Lua based, but it is relatively simple (effectively same as plain TeX answer of egreg, but ConTeXt first changes the catcode table to vrbcatcodes and uses a configurable stopper; I really don't understand why nested buffers work).
    – Aditya
    Sep 4, 2012 at 15:18
2

The scontents package tries to imitate the concept of ConText buffers from the LaTeX side.

\documentclass{article}
\usepackage{scontents}
\begin{document}
% If you need a environment version (like a \startbuffer ... \stopbuffer)
\begin{scontents}[store-env=main]
Something for main A.
\end{scontents}

% If you need a macro version (no verbatim)
\Scontents[store-cmd=main]{Something for main B.}

% or a macro with verbatim
\Scontents*[store-cmd=main]{Something for \verb|main C|.}

% Let's print them (like a \getbuffer)
This is first stored in main: \getstored[1]{main}\par
This is third stored in main: \getstored[3]{main}\par

% Print all of stored in main
\foreachsc[sep={\\[10pt]}]{main}

% Print source content (like a \typebuffer)
\typestored[3]{main}\par
\typestored[2]{main}\par
\typestored[1]{main}
\end{document}

The environment and starred macro version support verbatim content, the environment can be nested but cannot start and end on the same line. With both versions (environment and starred) it is also possible to write content to an external file. The non starred macro version has no support for verbatim content.

(I leave this answer here only because this post served as general inspiration for scontents)

1
  • Unfortunately the scontents environment is not extensible (use in a newly defined environment), VerbatimOut can be used instead (although it have to use a temporary file)
    – user202729
    Sep 12, 2021 at 2:59
1

Wheee another one for the #-rule:

\documentclass{article}
\def\dosomething{inside,}
\def\dosomethingelse{and outside the group}
\def\foo#{\bgroup\dosomething\aftergroup\dosomethingelse\let\next= }

\begin{document}
\foo{
  \verb|bar|

  \verb|baz|
}
\end{document}
2
  • 2
    I think you have it the wrong way round. The OP wants to know how to turn an environment into a macro argument, and you are showing how to turn a macro argument into an environment ;-) Sep 4, 2012 at 17:50
  • 1
    @StephanLehmke: That \vbox threw me off. Still, I think it's nice enough to mention.
    – morbusg
    Sep 5, 2012 at 3:53

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.