4

I have a command as follows:

\newcommand{\Comment}[1]{&&&#1\tabularnewline\hline}

And now I want to convert it to be an environment as follows:

\newsavebox{\tempDescription}
\newenvironment{Description}
{%
\begin{lrbox}{\tempDescription}%
\ignorespaces%
}
{%
\end{tempDescription}%
&&&\usebox{\tempDescription}\tabularnewline\hline%
}

The complete code is given below:

\documentclass[dvips,dvipsnames,rgb,table]{book}

\usepackage[a4paper,hmargin=10mm,vmargin=40mm,showframe]{geometry}
\usepackage{longtable}
\usepackage{array}
\usepackage{calc}
\usepackage{lscape}
\setlength{\tabcolsep}{10pt}
\setlength{\arrayrulewidth}{1pt}
\newcounter{No}
\renewcommand{\theNo}{\arabic{No}}
\newenvironment{MyTable}[4]%
{%
    \newcolumntype{O}[1]%
    {%
        >{%
            \begin{minipage}%
            {%
                    ##1\linewidth-2\tabcolsep-1.5\arrayrulewidth%
            }%
            \vspace{\tabcolsep}%
         }%
        c%
        <{%
                \vspace{\tabcolsep}%
                \end{minipage}%
         }%
    }%
    \newcolumntype{I}[1]%
    {%
        >{%
            \begin{minipage}%
            {%
                    ##1\linewidth-2\tabcolsep-\arrayrulewidth%
            }%
            \vspace{\tabcolsep}%
         }%
        c%
        <{%
                \vspace{\tabcolsep}%
                \end{minipage}%
         }%
    }%
    \setcounter{No}{0}%comment out this if you want to continuous numbering for all tables.
    \begin{longtable}%
    {%
            |>{\stepcounter{No}\centering\scriptsize\theNo}O{#1}<{}%
            |>{\centering}I{#2}<{\input{\jobname.tmp}}%
            |>{\centering\lstinputlisting{\jobname.tmp}}I{#3}<{}%
            |>{\scriptsize}O{#4}<{}%
            |%
    }%
    \hline\ignorespaces%
}%
{%
    \end{longtable}%
}

\newcommand{\Comment}[1]{&&&#1\tabularnewline\hline}

\newsavebox{\tempDescription}
\newenvironment{Description}
{%
\begin{lrbox}{\tempDescription}%
\ignorespaces%
}
{%
\end{tempDescription}%
&&&\usebox{\tempDescription}\tabularnewline\hline%
}

\usepackage{listings}
\lstset{%
language={PSTricks},
breaklines=true,
basicstyle=\ttfamily\scriptsize,%
keywordstyle=\color{blue},
backgroundcolor=\color{yellow!30}%
}
\usepackage{fancyvrb}


\def\MyRow{%        
        \VerbatimEnvironment%
        \begin{VerbatimOut}{\jobname.tmp}%
}

\def\endMyRow{%
        \end{VerbatimOut}%      
}



\usepackage{pstricks,pst-node}
\newpsstyle{gridstyle}{%
gridwidth=0.4pt,%default: 0.8pt
gridcolor=Red!20,%default: black
griddots=0,%default: 0 
%
gridlabels=3pt,%default: 10pt
gridlabelcolor=Blue,%default: black
%
subgriddiv=5,%default: 5
subgridwidth=0.2pt,%default: 0.4pt
subgridcolor=Green!20,%default: gray
subgriddots=0%default: 0
}

\usepackage{lipsum}


\begin{document}
%\clearpage
%\pagestyle{empty}
%Landscape starts here.
%\begin{landscape}
\arrayrulecolor{Red}
\begin{MyTable}{0.05}{0.3}{0.3}{0.35}%
%=============
\begin{MyRow}
\pspicture*[showgrid](3,3)
\pnode(1,1){A}
\pnode(3,3){B}
\ncline{A}{B}
\endpspicture
\end{MyRow}
\Comment{\lipsum[1]}
%=============
\begin{MyRow}
\begin{pspicture}[showgrid](3,3)
\psframe*[linecolor=red!30](3,2)
\end{pspicture}
\end{MyRow}
\Comment{\lipsum[2]}
%=============
\begin{MyRow}
\pspicture[showgrid](3,3)
\psframe*[linecolor=green!30](3,2)
\endpspicture
\end{MyRow}
\Comment{\lipsum[3]}
%=============
\begin{MyRow}
\pspicture[showgrid](3,3)
\psframe*[linecolor=Yellow](3,2)
\endpspicture
\end{MyRow}
\Comment{%
\begin{equation}
\int_a^b f(x)\, \textrm{d}x=F(b)-F(a)
\end{equation}
is the fundamental theorem of calculus.
}
%=============
\begin{MyRow}
\pspicture[showgrid](3,3)
\psframe*[linecolor=Maroon!30](3,2)
\endpspicture
\end{MyRow}
\begin{Description}%{%
Today I ate burnt bread. It was so delicious.
\begin{equation}
\int_a^b f(x)\, \textrm{d}x=F(b)-F(a)
\end{equation}
is the fundamental theorem of calculus.
%}
\end{Description}
%=============
\end{MyTable}
%\end{landscape}
%Landscape stops here.
%\pagestyle{plain}
\end{document}

Why cannot it be converted?

2 Answers 2

4

Quick answer:

\usepackage{environ}
\NewEnviron{Description}{%
  \expandafter\gdef\expandafter\gtemp\expandafter{%
    \expandafter&\expandafter&\expandafter&\BODY\tabularnewline\hline
  }%
  \aftergroup\gtemp
}

It does not allow verbatim text, but the package cprotect can fix that. My first attempt accepted verbatim directly, but using lrbox only allows one-line boxes (not quite true).

We use Will Robertson's environ package to define the Description environment. It reads the whole content of the environment (until reaching \end{Description}) and stores it in \BODY. Naively, we would want

\NewEnviron{Description}{%
  &&&\BODY\tabularnewline\hline}

But this fails: &&&\BODY\tabularnewline\hline is created inside a group, and the whole thing behaves somewhat like {&&&\BODY...} rather than &&&\BODY.... We want to get the & out of the group. This is done using the \aftergroup primitive: {...\aftergroup\a} gives {...}\a, i.e., \a is executed after the group is finished.

So, we store what we want to do after inside a command, \gtemp, and do \aftergroup\gtemp. You would thus think that the correct code is

\NewEnviron{Description}{%
  \gdef\gtemp{%
    &&&\BODY\tabularnewline\hline
  }%
  \aftergroup\gtemp
}

But it is not the end yet. \BODY is only the body of our environment while we are inside the group. When we try executing &&&\BODY\tabularnewline\hline outside the group, we get an error: \BODY is not defined. What should we do? We need to replace \BODY by its expansion (definition) while we are still inside the group. For this use \expandafter, which is quite practical, but messy to use. It tells TeX "see the next token? well... wait, do (expand) the next one first". A typical construction is \expandafter\a\expandafter\b\expandafter\c\expandafter\d\e. The first \expandafter reads \a, and expands the next token, \expandafter. This second \expandafter sees \b, but starts by expanding \expandafter, etc. In the end, \e is expanded first, and then \a, \b, \c, \d. (The various tokens don't get re-ordered: only the way they are expanded changes.)

So in our final code, which I repeat here,

\usepackage{environ}
\NewEnviron{Description}{%
  \expandafter\gdef\expandafter\gtemp\expandafter{%
    \expandafter&\expandafter&\expandafter&\BODY\tabularnewline\hline
  }%
  \aftergroup\gtemp
}

we first expand \BODY, and then \gdef\gtemp{&&&body\tabularnewline\hline}, where body is the real content of the environment (\BODY expanded). Finally, the \aftergroup\gtemp explained above will put this at the right place in the flow of the tabular.

4
  • @Bruno, do we need \ignorespaces in your solution? Jan 2, 2011 at 10:51
  • @xport: I must admit that I have no idea :(... Try with or without (remember to put \expandafter in front of everything before \BODY). Jan 2, 2011 at 10:56
  • @Bruno, I think we don't need ignorespaces anymore because the package author already used it internally. Jan 2, 2011 at 15:12
  • @xport:w Ok. Someday I'll learn about spacing in environments (recently I was mostly fighting with verbatim-like things). Jan 2, 2011 at 15:34
9

What you are doing doesn't work because of the way TeX parses arguments to go into an alignment (which is the technology underlying tables). When deciding what goes into a table cell, TeX reads one token with macro expansion on, checks it for special cases like \span, then it scans the text without macro expansion on up to the next & or \cr token. What it finds goes into a table cell, and is then processed the usual way, with macro expansion and all.

Your problem is that \begin{Description} does all sorts of setup before it gets around to executing \Description, and in particular \Description does not get expanded before TeX hits the first nonexpandable token resulting from expanding \begin. So TeX does not see the & tokens in there, and assumes it all goes into a single table cell.

You must log in to answer this question.

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