11

I want to typeset a book with some \parshape command, for example:

\documentclass{article}
\usepackage{lipsum}

\begin{document}
\parshape=4
0.2\textwidth 0.8\textwidth
0.2\textwidth 0.8\textwidth
0.2\textwidth 0.8\textwidth
0pt \textwidth
\lipsum
\end{document}

But since the lines and width may changes frequently, so I want to define a command like this

\newcounter{foo}
\setcounter{foo}{0}
\newcommand{\MyParShape}[3]
{
\parshape=#1
\loop
#2\textwidth #3\textwidth 
\addtocounter{foo}{1}
\if\thefoo<#1-1\repeat
0pt \textwidth
}

So I can call it like

\MyParShape{5}{0.5}{0.5}

or

\MyParShape{10}{0.8}{0.2}

But this does not work, I'm not an expert of TeX primitives, can somebody help?

9

As Harald says, the key here is that you need everything expanded. You can do that in a temporary variable, as he shows. Alternatively, you can use something like an expandable 'repeat n times' approach. Here, I'm assuming e-TeX is available

\documentclass{article}
\usepackage{lipsum}
\makeatletter
\newcommand*\replicate[1]{%
  \expandafter\replicate@aux\romannumeral\number\numexpr #1\relax000Q{}
}
\newcommand*\replicate@aux[1]{\csname replicate@aux@#1\endcsname}
\newcommand\replicate@aux@m{}
\long\def\replicate@aux@m#1Q#2#3{\replicate@aux#1Q{#2#3}{#3}}
\newcommand\replicate@aux@Q[2]{#1}
\DeclareRobustCommand*\MyParShape[3]{%
    \parshape = #1
      \replicate{#1 -1}{#2\textwidth #3\textwidth}%
      0pt\textwidth
}
\newcommand*\my@parshape{}
\makeatother
\begin{document}
\MyParShape{3}{0.6}{0.4}
\lipsum
\end{document}
5

If the paragraph shape you need is like that, it's simpler to obtain it with

\hangindent=.2\textwidth \hangafter=-3
  • Excellent point! That is why I chose a more complicated example in my answer, but I should have made it explicit. – Harald Hanche-Olsen May 13 '11 at 8:10
4

The reason your approach doesn't work is that it does not expand fully in TeX's mouth. Now building a fully expandable version might be rather involved. An easier approach is probably to first build up a command sequence containing your finished \parshape and the executing that. In the following complete example I use a token register:

\documentclass{article}
\usepackage{lipsum}

\newtoks\pstoks
\newcounter{pscnt}

\makeatletter
\newcommand{\parshapestart}[1]{\pstoks{\parshape=#1 }}
\newcommand{\parshapeappend}[1]{\pstoks\expandafter{\the\pstoks#1}}
\newcommand{\parshapeadd}[3]
  {
    \setcounter{pscnt}{0}
    \loop
    \parshapeappend{#2\textwidth #3\textwidth}
    \addtocounter{pscnt}{1}
    \ifnum\c@pscnt<#1\repeat
  }
\newcommand{\parshapeend}[1][0pt \textwidth]
  {\parshapeappend{#1}\the\pstoks}
\makeatother

\begin{document}

\parshapestart{6}
\parshapeadd{3}{0.2}{0.8}
\parshapeadd{2}{0.1}{0.9}
\parshapeend
\lipsum
\end{document}
2

For LaTeX (and plainTeX) there is the shapepar package which provides a set of predefined shapes but also allows you to define your own. However, it doesn't solve the issue with the unexpandable loop for you.

The command \shapepar should be used at the beginning of a paragraph, and it applies to the entire paragraph. There is one optional length parameter: a fixed scale, <scale len>; and one required parameter: a description of the shape, <shape spec>.

\shapepar [<scale len>]{<shape spec>} Text of the paragraph
0
\documentclass{article}
\usepackage{lipsum}
\newcount\shapectr
\newcommand\myParShape[3]{%
  \shapectr=0
  \def\shapeLength{}
  \loop
    \edef\shapeLength{\shapeLength #2\textwidth #3\textwidth }
    \advance\shapectr by 1
    \ifnum\the\shapectr<#1\relax
  \repeat
  \expandafter\parshape\numexpr #1+1\relax  
  \shapeLength
  0pt \textwidth%
}

\begin{document}
\myParShape{3}{0.2}{0.8}
\lipsum
\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.