5

I am trying to define a command \todopar[dummy]{msg} that fills a paragraph with dummy repeated a number of times and msg typeset centered in the paragraph.

The end result should look like this (without the spacing issues)

desiderata

My first attempt is

\documentclass[draft]{article}

\usepackage{lipsum}
\usepackage{xcolor}

\colorlet{todocol}{green!30!black}

\newcommand{\repetita}[1]{\leavevmode\xleaders\hbox{#1\ }\hfill\kern0pt}
\newcommand{\todopar}[2][To Do]{%
  \par\medskip\noindent{%
  \color{todocol!50!white}
  \null\repetita{#1}\\%
  \null\repetita{#1}\\%
  \null\repetita{#1}%
  \textcolor{todocol}{#2}%
  \repetita{#1}\\%
  \null\repetita{#1}\\%
  \null\repetita{#1}\par\medskip%
}}

\begin{document}
  \lipsum*[1]
  \todopar{This is somehing we have to do soooo bad that takes even more than one line oh my gosh.}
  \lipsum*[1]
  \todopar{Lot of work}
  \lipsum*[1]
\end{document}

Unfortunately, using \xleaders I do not get the dummy text in the central line aligned with the ones in the rest of the paragraph, plus the contents of the hbox of the leaders end with a space which means that the last repetition will end the line with a space as well.

How can I solve these two problems? Is there a more elegant way to implement this effect?

6

I'd use a paragraph rather than leaders:

enter image description here

\documentclass[draft]{article}

\usepackage{lipsum}
\usepackage{xcolor}

\colorlet{todocol}{green!30!black}


\newcommand{\todopar}[2][To Do]{%
% end the previous par and start a local group
  \par\medskip{%
% special para uses full length lines, no white space
% at start or end
  \parindent0pt
  \parfillskip0pt
% set colour for the filler text
  \color{todocol!50!white}%
% save filler text in box, for measuring
  \sbox0{#1 }%
% 
% Set the message text in a normal paragraph with normal
% \parfillskip to allow the last line to be short as usual.
% the whole construct is inside a scratch box 2.
  \setbox2\vbox{\parfillskip\fill\strut#2\unskip\strut
% Finish the paragraph with the message text
  \par
% Probably not needed but remove any glue or penalty
% after the last line
  \unskip\unpenalty
% remove the last (or only) line of the message paragraph
  \setbox0\lastbox
% 
% At this point box0 is always full width but contains the last
% line of the message followed by \rightskip and \parfillskip glue
% so \unskip twice and globally save box1 with the natural length
% of the last line of the message.
  \global\setbox1\hbox{\unhbox0\unskip\unskip}%
% 
% Now remove the baselineskip glue before that last line.
  \unskip\unpenalty\unskip\unpenalty
% and finish the box
  }%
% Now box2 contains all the lines of the message except the last
% so for a short message it will be empty
% 
% see how many filler text it takes for 2 lines of text
  \count0=\dimexpr2\columnwidth\relax
  \divide\count0 \wd0 %
% 
% see how many filler text it takes to pad one side
% of a line that has the (last line of) the message in
% the middle
  \count2=\dimexpr(\columnwidth-\wd1-2em)/2\relax
  \divide\count2 \wd0 %
% 
% save the filler text, use a macro rather than the box
% to allow any white space in the filler to stretch
  \def\tmp{\strut#1 }%
% 
% make a paragraph of \count0 copies of the filler making
% a two full length  line paragraph
  \xrep{\count0}\par
% 
% unbox box 2 this is the initial full length lines of the message
% in the main color. As this is unboxed it is a normal sequence of
% line boxes and glue so can break at a page. 
  {\color{todocol}\unvbox2}%
% 
% Now make a 1-line paragraph with count2 copies of filler
% either side of the last line of the message, with a word space 
% padding either side/
  \xrep{\count2} \textcolor{todocol}{\unhbox1} \xrep{\count2}\par
% 
% As before a 2 line filler paragraph
  \xrep{\count0}\par%
% 
% end the local group
  }%
% A skip to match the one at the start.
 \medskip}

% A simple recursive loop macro, makes #1
% copies of \tmp
\def\xrep#1{%
\ifnum#1>0 
\tmp
\xrep{\numexpr#1-1\relax}%
\fi}

% A test document....
\begin{document}
  \lipsum*[1]
  \todopar{This is somehing we have to do soooo bad that takes even more than one line oh my gosh.}
  \lipsum*[1]
  \todopar{Lot of work}
  \lipsum*[1]
\end{document}
  • thanks, this gives a great result although I still have to understand every line. I would put \par\medskip before changing \parindent and \parfillskip so the previous paragraph is unaltered even if no space occurs between it and \todopar – Bordaigorl Feb 22 '15 at 22:37
  • I would also use \linewidth instead of \textwidth to support two column layouts – Bordaigorl Feb 22 '15 at 22:58
  • 2
    @Bordaigorl Use \columnwidth that is good in both cases. – egreg Feb 22 '15 at 23:03
  • @DavidCarlisle could you briefly explain how you construct the boxes? I kind of get the calculations on the widths and \xrep but I am not sure how you manage to construct the centered paragraph so that it can break in multiple lines and even across pages – Bordaigorl Feb 22 '15 at 23:32
  • @Bordaigorl I'll add some comments to the code.... – David Carlisle Feb 22 '15 at 23:41

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.