7

I'm trying to write a \multicenter macro that acts as \centerline, except that it splits the text in different lines whenever it encounters the command \\. For example, \multicenter{ciao\\a\\tutti!} should be equivalent to \centerline{ciao}\centerline{a}\centerline{tutti!}.

I came up with the following code:

\def\multicenter#1{\def\\{\egroup\centerline\bgroup}\centerline{#1}}

but it does not work as expected. What's going on?

6

\centerline is a macro with one argument; you can use the “expanded” definition which uses \line (that is, \hbox to \hsize):

\def\multicenter#1{%
  \par
  \bgroup % keep changes local
  \def\\{\hss\egroup\line\bgroup\hss}%
  \line{\hss#1\hss}%
  \egroup
}

\centerline{ciao}
\centerline{a}
\centerline{tutti}

\multicenter{ciao\\a\\tutti}

\bye

enter image description here

Allowing also spaces around \\:

\def\multicenter#1{%
  \par
  \bgroup % keep changes local
  \def\\{\unskip\hss\egroup\line\bgroup\hss\ignorespaces}%
  \line{\hss#1\hss}%
  \egroup
}

\centerline{ciao}
\centerline{a}
\centerline{tutti}

\multicenter{ciao \\ a \\ tutti}

\bye

What happens with your macro? Let's try the simpler \multicenter{ciao\\mondo}, which becomes

\def\\{\egroup\centerline\bgroup}\centerline{ciao\\mondo}}

The definition is stored and \centerline is expanded according to \def\centerline#1{\line{\hss#1\hss}}, so we get

\line{\hss ciao\\mondo\hss}

that becomes

\hbox to \hsize{\hss ciao\\mondo\hss}

The box is started and upon expanding \\ we get (with a few simplifications)

\hbox to\hsize{\hss ciao\egroup\centerline\bgroup mondo\hss}

So the box is closed and “ciao” will be flush right. Next \centerline is expanded, with \bgroup as argument, so we have

\line{\hss\bgroup\hss}mondo\hss}

and so

\hbox to\hsize{\hss\bgroup\hss}mondo\hss}

The closing brace matches \bgroup and you get mondo not centered, because of the two \hss in front of it.

Note that \hbox<box specification>{<horizontal material} allows the horizontal material to be surrounded either by explicit or implicit braces; the braces around a macro argument must always be explicit.


Just for fun, here's another solution (it relies on \parfillskip having its default value, but it can be easily generalized)

\def\multicenter#1{%
  \par
  \begingroup
    \leftskip=\parfillskip \parindent=0pt
    \let\\=\par
    #1\par
  \endgroup
  }
}

The difference with the above solution is that here overlong lines will wrap, whereas in the \hss based solution above, they will stick in the margins.

An \halign based solution that's similar to the \hss based one:

\def\multicenter#1{%
  \par
  \begingroup
  \let\\=\cr
  \tabskip=0pt plus 1fil
  \halign to\hsize{\hidewidth\ignorespaces##\unskip\hidewidth\cr#1\crcr}
  \endgroup
}

Note that both new solutions allow for spaces around \\.

| improve this answer | |
7

You can not surround undelimited (normal) macro arguments with \bgroup\egroup you have to use {}

I would use an \halign something like

\def\mcenterline#1{\par{%
   \tabskip0pt plus 1fill
  \let\\\cr\halign to \hsize{\hfill##\hfill\cr#1\crcr}}%
 \par}

\noindent a\dotfill a

\mcenterline{aaa\\bb\\xxx x x x}

\bye

To see what is wrong with the original consider:

\def\multicenter#1{\def\\{\egroup\centerline\bgroup}\centerline{#1}}

\tracingall
\multicenter{aaa\\bb\\xxx x x x}

\bye

which produces

\multicenter #1->\def \\{\egroup \centerline \bgroup }\centerline {#1}
#1<-aaa\\bb\\xxx x x x
{\def}
{changing \\=macro:#1pt->#1}
{into \\=macro:->\egroup \centerline \bgroup }

\centerline #1->\line {\hss #1\hss }
#1<-aaa\\bb\\xxx x x x

\line ->\hbox to\hsize 
{\hbox}
{entering adjusted hbox group (level 1) at line 4}
{restricted horizontal mode: \hss}
{the letter a}

\\->\egroup \centerline \bgroup 
{end-group character }}
{leaving adjusted hbox group (level 1) entered at line 4}
%% goal height=643.20255, max depth=4.0

\centerline #1->\line {\hss #1\hss }
#1<-\bgroup 

\line ->\hbox to\hsize 
{vertical mode: \hbox}
{entering adjusted hbox group (level 1) at line 4}
{restricted horizontal mode: \hss}
{begin-group character {}
{entering simple group (level 2) at line 4}
{\hss}
{end-group character }}
{leaving simple group (level 2) entered at line 4}
{the letter b}

\\->\egroup \centerline \bgroup 
{end-group character }}
{leaving adjusted hbox group (level 1) entered at line 4}
% t=10.0 g=643.20255 b=10000 p=0 c=100000#

\centerline #1->\line {\hss #1\hss }
#1<-\bgroup 

\line ->\hbox to\hsize 
{vertical mode: \hbox}
{entering adjusted hbox group (level 1) at line 4}
{restricted horizontal mode: \hss}
{begin-group character {}
{entering simple group (level 2) at line 4}
{\hss}
{end-group character }}
{leaving simple group (level 2) entered at line 4}
{the letter x}
{blank space  }
{the letter x}
{blank space  }
{the letter x}
{blank space  }
{the letter x}
{\hss}
{end-group character }}
{leaving adjusted hbox group (level 1) entered at line 4}
% t=22.0 g=643.20255 b=10000 p=0 c=100000#
{vertical mode: blank space  }
{\par}

Note in particular that #1 in the \centerline is just \bgroup not all the text between \bgroup and \egroup so after that the expansion is more or less all accidental and undesired.

| improve this answer | |
  • If \bgroup and \endgroup can't be used this way, what does my macro expand to, then? – User Oct 6 '15 at 16:12
  • @Matteo the wrong thing:-) I'll add a trace to my answer in a second – David Carlisle Oct 6 '15 at 16:15
  • @Matteo see updated answer – David Carlisle Oct 6 '15 at 16:18

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.