Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
add comment

5 Answers

up vote 7 down vote accepted

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}
share|improve this answer
add comment

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

\hangindent=.2\textwidth \hangafter=-3
share|improve this answer
    
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
add comment

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}
share|improve this answer
    
You can avoid the counter by using an expandable repetition routine, as seen in tex.stackexchange.com/questions/16189/repeat-command-n-times. My first approach was to do that inside an \edef, but as you posted a similar answer I've gone for avoiding additional storage and using full expansion! –  Joseph Wright May 13 '11 at 7:19
add comment

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
share|improve this answer
add comment
\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}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.