3

When using a \parshape, I can use

\newline\hspace*{0.5\parindent}

to produce a new "paragraph" and still continue with the current \parshape.

Is it possible to leave a blank line and still get the effect of the above? That is, the blank line should not become a \par as that resets the \parshape.

The MWE below (with the \par commented out and the \newline\hspace*{0.5\parindent} uncommented) produces the desired output:

enter image description here

but I would like to be able to leave a blank line and when using a \parshape, automatically use some macro that yields the desired behavior.

Note:

  • This is used for a few paragraphs which fit on one page.
  • Besides visually being easier to read/edit, having a blank line also allows for regular typesetting as well. I need to be able to typeset the content in this environment normally as well.

Code:

\documentclass{article}
\usepackage{showframe}

\newcommand*{\Text}{%
\sloppy\noindent
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis. 
\par% <-- would prefer this though, or even better a blank line
%\newline\hspace*{0.5\parindent}% <-- Replacing the \par with this works
Curabitur dictum
gravida mauris. Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna. 
Donec vehicula augue eu neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. 
Nulla et lectus vestibulum urna fringilla ultrices.%
}%


\newcommand*{\TitleParShape}{%
    0.40\hsize 0.20\hsize
}%
\newcommand*{\BodyParShape}{%
    0.35\hsize 0.30\hsize
    0.30\hsize 0.40\hsize
    0.25\hsize 0.50\hsize
    0.20\hsize 0.60\hsize
    0.15\hsize 0.70\hsize
    0.10\hsize 0.80\hsize
    0.05\hsize 0.90\hsize
    0.00\hsize 1.00\hsize
}%

\newcommand*{\MyParShape}{%
    \parshape 9
    \TitleParShape
    \BodyParShape
}%

\begin{document}
\parshape 1
\TitleParShape
{\bfseries\centering My Title\par\smallskip}%
\parshape 8
\BodyParShape
\Text
\end{document}
  • what is the purpose of the \hspace*{0.5\parindent}? it applies only to one text line? – user4686 Nov 14 '18 at 16:45
  • @jfbu I guess it is to highlight the new paragraph (without the line skipped like in your answer), but the guess part is important, I'm not certain. – Skillmon Nov 14 '18 at 17:41
  • @jfbu: It was intended to fake a new paragraph. – Peter Grill Nov 14 '18 at 17:49
3

Rewrite working in more cases

I've removed the original answer and added this rewrite. The rewrite should work with more par shapes (the original answer only worked for par shapes with up to 8 definitions).

How it works:

I'm redefining the \par macro to insert some code (so use with caution, if in doubt group it, the redefinition is made local).

\documentclass{article}
\usepackage{showframe}

\newcommand*{\Text}{%
\sloppy\noindent
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis. 

Curabitur dictum
gravida mauris. Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna. 
Donec vehicula augue eu neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. 
Nulla et lectus vestibulum urna fringilla ultrices.%
}%


\makeatletter

\@ifdefinable\peterCountA{\newcount\peterCountA}% stores lines already done
\@ifdefinable\peterCountB{\newcount\peterCountB}% stores prevgraf
\@ifdefinable\peterCountC{\newcount\peterCountC}% stores BodyParShape number

\newcommand*{\TitleParShape}{%
    0.40\hsize 0.20\hsize
}%

\peterCountC=8 % number of items in the BodyParShape
\newcommand*{\BodyParShape}{%
    0.35\hsize 0.30\hsize
    0.30\hsize 0.40\hsize
    0.25\hsize 0.50\hsize
    0.20\hsize 0.60\hsize
    0.15\hsize 0.70\hsize
    0.10\hsize 0.80\hsize
    0.05\hsize 0.90\hsize
    0.00\hsize 1.00\hsize
}

\@ifdefinable\eatParShapeLine
  {\long\def\eatParShapeLine#1\hsize #2\hsize {}}
\@ifdefinable\myif@fiAa % equivalent to \expandafter\@firstoftwo
  {\long\def\myif@fiAa#1\fi#2#3{\fi#2}}
\@ifdefinable\myif@fiBb % equivalent to \expandafter\@secondoftwo
  {\long\def\myif@fiBb\fi#1#2{\fi#2}}

% helper macro to expand the first token in the last argument once
\newcommand\myexp@NnV[3]
  {%
    \expandafter\myexp@NnV@\expandafter{#3}{#1}{#2}%
  }
\newcommand\myexp@NnV@[3]
  {%
    #2{#3}{#1}%
  }
% a wrapper around \eatParShapeLine to eat the first #1 lines in the parshape #2
\newcommand\eatParShapeLines[2]
  {%
    \myexp@NnV\eatParShapeLinesAux{#1}{#2}%
  }
% expandable loop
\newcommand\eatParShapeLinesAux[2]
  {%
    % if still loops to do
    \ifnum#1>0
      % reinsert the first token/group after \fi
      \myif@fiAa
    \else
      % reinsert the second token/group after \fi
      \myif@fiBb
    \fi
    {%
      % reinsert the \eatParShapeLinesAux with #1 decreased by 1 and the first
      % two \hsize fractions from #2 eaten
      \expandafter\myexp@NnV\expandafter\eatParShapeLinesAux\expandafter
        {\the\numexpr#1-1}{\eatParShapeLine#2}%
    }
    % loop is done, reinsert the shortened parshape
    {#2}%
  }
\newcommand\BodyParShapeNum[2]
  {%
    % \parshape expands its arguments to find numbers and dimensions
    \parshape #1 \eatParShapeLines{#2}\BodyParShape
  }

\newcommand*{\MyParShape}{%
    \parshape 9
    \TitleParShape
    \BodyParShape
}%

\newcommand\ParshapeMultiPar
  {%
    % setup initial BodyParShape
    \parshape \peterCountC \BodyParShape
    % no lines processed yet
    \peterCountA=0
    % redefine \par to reinsert the left over parshape entries
    \def\par
      {%
        % end the previous paragraph
        \endgraf
        % save the \prevgraf counter (lines of last paragraph)
        \peterCountB\prevgraf
        % check whether there are still lines left from BodyParShape
        \ifnum\numexpr\peterCountC-\peterCountA>\peterCountB
          % update lines already done
          \advance\peterCountA\peterCountB
          % set the parshape for the next par, leave out first \peterCountA
          % lines
          \expandafter\BodyParShapeNum\expandafter
            {\the\numexpr\peterCountC-\peterCountA}{\peterCountA}%
          % fake a parindent
          \noindent\hspace*{.5\parindent}
        \else
          % else is executed if the BodyParShape was ended in the previous
          % paragraph. In this case revert the redefinition of \par
          \let\par\endgraf
        \fi
      }%
  }
\makeatother

\begin{document}
\parshape 1
\TitleParShape
{\bfseries\centering My Title\par\smallskip}%
\parshape 8
\BodyParShape
\Text

\ParshapeMultiPar
\Text

\end{document}

enter image description here

  • This seems to work well, even without the duckuments package. :-) Can you add a brief explanation as to how it works as it is not obvious by looking at the code? – Peter Grill Nov 14 '18 at 18:13
  • I added the duckuments package sometime during testing and didn't remove it, sorry. I'll add a slight rewrite soonish. – Skillmon Nov 14 '18 at 18:32
  • @PeterGrill I rewrote the code. The first version had its limitations (max. 8 parshape entries). Also the code is now commented, I hope the comments are enough explanation, if something's left, feel free to ask. – Skillmon Nov 14 '18 at 19:35
  • @PeterGrill just found out that the code might bug if your \Text is in a group but the \ParshapeMultiPar isn't (so \ParshapeMultiPar\begingroup\Text\endgroup is no good idea). – Skillmon Nov 14 '18 at 21:34
2

Not sure if this what you want. I added a blank line in output via a \parskip setting. I removed from \Text all formatting as you say you want to use macro elsewhere. The \resumebody by default does nothing, but it can be used to resume some parshape. (not much needs to be known on the parshape, perhaps the number of lines it deals with; I did not add error checking if previous paragraph already exhausted the parshape.).

\documentclass{article}
\usepackage{showframe}

\newcommand*{\Text}{%
  Lorem ipsum dolor sit amet, consectetuer adipiscing
  elit. Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae,
  felis.

  \resumebody
  Curabitur dictum gravida mauris. Nam arcu libero, nonummy eget,
  consectetuer id, vulputate a, magna. Donec vehicula augue eu neque.
  Pellentesque habitant morbi tristique senectus et netus et malesuada
  fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus
  sem. Nulla et lectus vestibulum urna fringilla ultrices.%
}%


\newcommand*{\TitleParShape}{%
    0.40\hsize 0.20\hsize
}%
\newcommand*{\BodyParShape}{%
    0.35\hsize 0.30\hsize % 10 tokens per line spec
    0.30\hsize 0.40\hsize
    0.25\hsize 0.50\hsize
    0.20\hsize 0.60\hsize
    0.15\hsize 0.70\hsize
    0.10\hsize 0.80\hsize
    0.05\hsize 0.90\hsize
    0.00\hsize 1.00\hsize
}%

\newcommand*{\MyParShape}{%
    \parshape 9
    \TitleParShape
    \BodyParShape
}%

\usepackage{xinttools}

\newcommand*{\resumebodyparshape}{%
   \parshape \numexpr8-\prevgraf-1\relax
   \xintTrim{10*\prevgraf+10}{\BodyParShape}%
}

\newcommand*{\resumebody}{}

\begin{document}
\parshape 1
\TitleParShape
{\bfseries\centering My Title\par\smallskip}%
\parshape 8
\BodyParShape
\let\resumebody\resumebodyparshape
\edef\currentparfillskip{\the\parfillskip}\parfillskip0pt
\edef\currentparskip{\the\parskip}\parskip\baselineskip
  \sloppy
  \noindent 
  \Text
\parfillskip\currentparfillskip
\par
\parskip\currentparskip
\let\resumebody\empty
\end{document}

I used xinttools per convenience, even more light-weight package xintkernel is enough via its \xintgobble macro. (but then I need to do add a bit of \expandafter for expanding \BodyParShape before gobbling, i.e. this works:

\usepackage{xintkernel}

\newcommand*{\resumebodyparshape}{%
   \edef\temp{%
   \parshape \numexpr8-\prevgraf-1\relax
   \noexpand\romannumeral\noexpand\xintgobble{10*\prevgraf+10}\BodyParShape}%
   \temp
}

or this

\usepackage{xintkernel}

\newcommand*{\resumebodyparshape}{%
   \parshape \numexpr8-\prevgraf-1\relax
   \expandafter\resumebodyaux\expandafter{\BodyParShape}%
}%

\def\resumebodyaux#1{\romannumeral\xintgobble{10*\prevgraf+10}#1}

and needs only xintkernel package.)

enter image description here

  • I am removing from \BodyParShape one more line than the previous paragraphs had. Perhaps this "one more" is not what @PeterGrill asked. But it felt more coherent this way. – user4686 Nov 14 '18 at 17:17
  • The LaTeX \list code uses the fact that {\par} (or rather {\@@par} for them) does not kill the parshape. But it starts over from the first line spec, it does not continue where it stopped. Try it by ending first par with {%, leaving blank line and then a line with }% and continuing on next line. – user4686 Nov 14 '18 at 17:30
  • I am assuming the 8 is the number of lines in the \parshape, but what is the 10? Also, don't think this will work if there is more than one paragraph. – Peter Grill Nov 14 '18 at 18:07
  • Yes this will not work as is for more than more preceding paragraph. The 10 is the number of tokens in 0.xy\hsize 0.rs\hsize. The -1 (or +10) is because I deliberately skip one of the parshape line to correspond to empty line in output (obtained via \parskip). – user4686 Nov 14 '18 at 18:29
  • (I fiddled with \parfillskip quite deliberately to force the first paragraph last line to use the full width as allowed by the parshape, I see from other answers that this may not be the expectation, but I felt it was better in accord with interrupting and resuming a parshape) – user4686 Nov 15 '18 at 8:39
2

If you're brave there is l3galley… I think there are too many issues with l3galley to really use it yet, but it shows what might be possible in the future.

Some notes:

  • I've used internal variables to stop the cutout at the end of the environment (in case of short paragraphs). This seems like a bad idea, but I can't make the cutouts stop any other way.
  • l3galley breaks stuff.

MWE

\documentclass{article}
\usepackage{xparse}
\usepackage{l3galley}
\ExplSyntaxOn
\clist_new:N \l__grill_indents_clist
\clist_set:Nn \l__grill_indents_clist
{
  0.35\hsize, 0.30\hsize, 0.25\hsize, 0.20\hsize,
  0.15\hsize, 0.10\hsize, 0.05\hsize, 0.00\hsize
}
\NewDocumentEnvironment { pyramid } { }
{
  \dim_set:Nn \l_galley_par_indent_dim { 0pt }
  \use:x
  {
    \exp_not:N \galley_cutout_left:nn { 0 }
    {
      \clist_use:Nn \l__grill_indents_clist { , }
    }
  }
  \use:x
  {
    \exp_not:N \galley_cutout_right:nn { 0 }
    {
      \clist_use:Nn \l__grill_indents_clist { , }
    }
  }
  \sloppy
}
{
  \galley_par:
  \seq_gclear:N \g__galley_cutout_left_seq
  \seq_gclear:N \g__galley_cutout_right_seq
  \bool_gset_false:N \g__galley_cutout_active_bool
}
\ExplSyntaxOff
\begin{document}
\begin{pyramid}
  Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit,
  vestibulum ut, placerat ac, adipiscing vitae, felis.

  Curabitur dictum gravida mauris. Nam arcu libero, nonummy eget, consectetuer
  id, vulputate a, magna. Donec vehicula augue eu neque. Pellentesque habitant
  morbi tristique senectus et netus et malesuada fames ac turpis egestas.
  Mauris ut leo. Cras viverra metus rhoncus sem. Nulla et lectus vestibulum
  urna fringilla ultrices.
\end{pyramid}

\end{document}

output

  • Is l3galley considered experimental? Don't see that mentioned in the documentation. Also, are the cases where l3galley is known to break things listed somewhere? – Peter Grill Nov 15 '18 at 5:50
  • @PeterGrill, yes. It is in the l3experimental bundle. I don't know of a list of things that break, but there are the relevant issues on GitHub. – David Purton Nov 15 '18 at 6:00
  • The l3experimental bundle link mentions xgalley, not l3galley. Are they perhaps the same? Not sure why the experimental note can not be in each of the packages as well? – Peter Grill Nov 15 '18 at 6:24
  • @PeterGrill, xgalley contains some higher level templates built on top of l3galley. Both are part of the experimental LaTeX 3 packages. – David Purton Nov 15 '18 at 7:21

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.