11

I am trying to design a box that has a semicircular parshape at the top and bottom as shown here:

enter image description here

If the text is small enough then this is intended to shrink to a circle of the given \Radius (and not any smaller):

enter image description here

Ideal solution would be if tcolorbox could be used to produce this, but did not see anything in the documentation regarding a rounded \parshape.

Notes:

The code to produce the first image above given in the MWE, but is has some issues including:

  • The bottom \parshape was manually applied at the correct point.
  • The \PaperHeight is determined by trial and error. Goal is to get the height is to increase (or decrease to 2\Radius) to fit the entire contents in one such box (ie, no multi-page issues).
  • The title is assumed to be able to fit in 2 lines. If it requires more than that then things don't work.

References:

Code:

\documentclass{article}

\usepackage{lipsum}
\usepackage{tikz}
\usepackage[protrusion=false]{microtype}
%\usepackage{showframe}

\usepackage[mathlines]{lineno}
\linenumbers

\newcommand*{\Radius}{1.75in}
\newcommand*{\Margins}{2pt}
\newcommand*{\ExtraLeftMargin}{0cm}% Set to 0.5cm if want to see line numbers

\newcommand*{\PaperHeight}{12.5cm}%<----- This is tweaked for this example

\newlength{\TextWidth}
\newlength{\PaperWidth}
\pgfmathsetlength{\TextWidth}{2*\Radius}
\pgfmathsetlength{\PaperWidth}{\TextWidth+2*\Margins}

\usepackage[
    paperwidth=\dimexpr\PaperWidth+\ExtraLeftMargin\relax,
    paperheight=\PaperHeight,
    left=\dimexpr\Margins+\ExtraLeftMargin\relax,
    right=\Margins,
    top=\Margins,
    bottom=\Margins,
]{geometry}

\tikzset{Frame Border Style/.style={thick, draw=red}}
\tikzset{Frame Fill Style/.style={fill=green!15}}
\tikzset{Frame Title Rule Style/.style={magenta, thin}}
\tikzset{Frame Title Fill Style/.style={fill=yellow!25}}

\newcommand*{\ExternalBorder}[1]{%
    \draw [#1]
        ([shift={(0.5*\ExtraLeftMargin-\Radius,-\Radius-\Margins)}]current page.north) 
        arc [start angle=180, end angle=0, radius=\Radius]
        -- ([shift={(0.5*\ExtraLeftMargin+\Radius,\Radius+\Margins)}]current page.south) 
        arc [start angle=0, end angle=-180, radius=\Radius]
        -- cycle
    ;
}%
\newcommand*{\TitleAndExternalBorder}[1]{%
    \noindent
    \begin{tikzpicture}[remember picture,overlay]
        \ExternalBorder{Frame Border Style, Frame Fill Style}
        \begin{scope}%% ------------------------------------ Title
            \clip 
                ([shift={(0.5*\ExtraLeftMargin,-\Radius-\Margins)}]current page.north) 
                    circle (\Radius);
            \draw [Frame Title Rule Style, Frame Title Fill Style] 
            ([shift={(-2*\Radius-\Margins+0.20*\TextWidth,-2.25*\baselineskip)}]current page.north east)
            rectangle ++(0.60*\TextWidth,2.25*\baselineskip-\Margins)
            ;
        \end{scope}
        \ExternalBorder{Frame Border Style}
    \end{tikzpicture}%
    \TitleShape\begingroup\centering\textbf{#1}\par\endgroup%
    \TopSemiCircleParshape%
}


\newcommand*{\TitleShape}{%
    \parshape 2 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
}%
\newcommand*{\TopSemiCircleParshape}{%
    \parshape 9 
    %% First two lines used by title
    %0.40\hsize 0.20\hsize
    %0.26\hsize 0.48\hsize 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    0.00\hsize 1.00\hsize 
}

\newcommand*{\BotSemiCircleParshape}{%
    \parshape 11 
    0.00\hsize 1.00\hsize 
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
}

\newcommand{\TextA}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum hendrerit augue at libero dignissim cursus. Vestibulum tristique metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci dolor lobortis lectus, in pellentesque ante est sit amet odio. Integer pretium iaculis sapien sit amet sagittis. Sed sagittis libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id dictum. Nulla facilisi. Proin aliquet semper odio, sit amet eleifend ante.}

\newcommand{\TextB}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum hendrerit augue at libero dignissim cursus. Vestibulum tristique metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci dolor lobor\dots}

\newenvironment{MyRoundedBox}[1]{%
    %% #1 = Title
    \sloppy\TitleAndExternalBorder{#1}%
}{}%

\begin{document}%
\begin{MyRoundedBox}{My Lipsum Title}
    \noindent\TextA
    \par\BotSemiCircleParshape\TextB\par
\end{MyRoundedBox}%
\end{document}
  • 1
    +1 Can't you just try out in a savebox, which has the parshape of the upper half circle and then the lower circle and then a box, by how much its height will larger than twice the radius? This will then be height of the vertical stretch. – user121799 Sep 14 '18 at 21:27
  • @marmot: Yes, that would be a good approximation as to the total height. – Peter Grill Sep 14 '18 at 21:30
  • @marmot: Follow-up question related to using a \savebox: Box with Semicircular Parshape in a Savebox. – Peter Grill Sep 15 '18 at 8:54
  • @marmot if you refer to LaTeX2e \newsavebox, \savebox, \sbox, the latter two are designed to use tacitly \hbox, so you would need a \parbox inside it to be able to specify a \parshape. – user4686 Sep 15 '18 at 9:04
  • Maybe this could be a source of inspiration; it is written in Italian, but what matters is the file chiusaastendardo_2015-04-07.tex that you can download from there, and is written in English. – GuM Sep 15 '18 at 9:59
7

In order to simplify things, I removed all TikZ decoration, but you can then reinstate it. Also I renamed a few macros, and removed \parshape from some for convenience of reuse.

Beware also that now \TextA and \TextB are merged in to \Text (which was objective), but paragraphs within are emulated with \newline, to not cancel current parshape.

The whole code aims at producing a single parshape: topcircle, intermediate zone, bottom circle.

\documentclass{article}

\newcommand*{\TitleParshapeLines}{%
%    \parshape 2 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
}%

\newcommand*{\TopSemiCircleParshapeLines}{%
%    \parshape 9 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    0.00\hsize 1.00\hsize 
}

\newcommand*{\BotSemiCircleParshapeLines}{%
%    \parshape 11 
    0.00\hsize 1.00\hsize 
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
}

\newcommand{\Text}{%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  dolor lobortis lectus, in pellentesque ante est sit amet odio.
  Integer pretium iaculis sapien sit amet sagittis. Sed sagittis
  libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac
  enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id
  dictum. Nulla facilisi. Proin aliquet semper odio, sit amet eleifend
  ante.\newline
  \hspace*{\parindent}%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  dolor lobor\dots
}

\newcommand*{\Radius}{1.75in}
\newcommand*{\Margins}{2pt}
\newcommand*{\ExtraLeftMargin}{0cm}% Set to 0.5cm if want to see line numbers


\newcommand\TextWidth{}
\edef\TextWidth{\the\dimexpr2\dimexpr\Radius}

\newcommand\PaperWidth{}
\edef\PaperWidth{\the\dimexpr\TextWidth+2\dimexpr\Margins}

\setbox0\vbox{\hsize\TextWidth\relax
  \sloppy\everypar{}\parskip0pt\relax
  \parshape
  21
  \TopSemiCircleParshapeLines
  \BotSemiCircleParshapeLines
  0pt \hsize
  \noindent\Text
  \par\xdef\NbOfTextLines{\the\prevgraf}%
}

\typeout{USING NBOFLINES = \NbOfTextLines}

%\newcommand*{\PaperHeight}{12.5cm}%<----- This is tweaked for this example

\newcommand\PaperHeight{}
% I assume here Title on 2 lines
\edef\PaperHeight{\the\dimexpr\numexpr2+\NbOfTextLines\relax\baselineskip}

\typeout{USING PAPERHEIGHT = \PaperHeight}

\usepackage[
    paperwidth=\dimexpr\PaperWidth+\ExtraLeftMargin\relax,
    paperheight=\PaperHeight,
    left=\dimexpr\Margins+\ExtraLeftMargin\relax,
    right=\Margins,
    top=\Margins,
    bottom=\Margins,
]{geometry}

\usepackage{xintkernel}

\edef\TextParshapeLines{%
  \TopSemiCircleParshapeLines
  \romannumeral\xintreplicate{\NbOfTextLines-20}{0pt \hsize}%
  \BotSemiCircleParshapeLines
}

\begin{document}
%\topskip0pt
%\vspace*{\fill}
\sloppy\parskip0pt\relax
    \parshape2 \TitleParshapeLines
    \begingroup\centering\textbf{My Lipsum Title}\par\endgroup
    \parshape
    \NbOfTextLines\space
    \TextParshapeLines
    \noindent\Text
    \par
%\vspace*{\fill}
\thispagestyle{empty}
\end{document}

Gives

enter image description here

Here is edited code, compatible with shorter text:

\documentclass{article}

\newcommand*{\TitleParshapeLines}{%
%    \parshape 2 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
}%

\newcommand*{\TopSemiCircleParshapeLines}{%
%    \parshape 9 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    0.00\hsize 1.00\hsize 
}

\newcommand*{\BotSemiCircleParshapeLines}{%
%    \parshape 11 
    0.00\hsize 1.00\hsize 
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
}

\newcommand{\Text}{%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  dolor lobortis lectus, in pellentesque ante est sit amet odio.
  Integer pretium iaculis sapien sit amet sagittis. Sed sagittis
  libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac
  enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id
  dictum. Nulla facilisi. Proin aliquet semper odio, sit amet eleifend
  ante.\newline
  \hspace*{\parindent}%
  % Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  % ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  % tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  % urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  % hendrerit augue at libero dignissim cursus. Vestibulum tristique
  % metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  % congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  % dolor lobor\dots
}

\newcommand*{\Radius}{1.75in}
\newcommand*{\Margins}{2pt}
\newcommand*{\ExtraLeftMargin}{0cm}% Set to 0.5cm if want to see line numbers


\newcommand\TextWidth{}
\edef\TextWidth{\the\dimexpr2\dimexpr\Radius}

\newcommand\PaperWidth{}
\edef\PaperWidth{\the\dimexpr\TextWidth+2\dimexpr\Margins}

\setbox0\vbox{\hsize\TextWidth\relax
  \sloppy\everypar{}\parskip0pt\relax
  \parshape
  21
  \TopSemiCircleParshapeLines
  \BotSemiCircleParshapeLines
  0pt \hsize
  \noindent\Text
  \par\xdef\NbOfTextLines{\the\prevgraf}%
}

\typeout{USING NBOFLINES = \NbOfTextLines}

%\newcommand*{\PaperHeight}{12.5cm}%<----- This is tweaked for this example

\newcommand\PaperHeight{}
% I assume here Title on 2 lines
\edef\PaperHeight{\the\dimexpr\numexpr2+
                      \ifnum\NbOfTextLines>20
                            \NbOfTextLines
                      \else 20\fi\relax\baselineskip}

\typeout{USING PAPERHEIGHT = \PaperHeight}

\usepackage[
    paperwidth=\dimexpr\PaperWidth+\ExtraLeftMargin\relax,
    paperheight=\PaperHeight,
    left=\dimexpr\Margins+\ExtraLeftMargin\relax,
    right=\Margins,
    top=\Margins,
    bottom=\Margins,
]{geometry}

\usepackage{xintkernel}

\edef\TextParshapeLines{%
  \TopSemiCircleParshapeLines
  \romannumeral\xintreplicate{\NbOfTextLines-20}{0pt \hsize}%
  \BotSemiCircleParshapeLines
}


%\show\TextParshapeLines
\begin{document}
%\topskip0pt
%\vspace*{\fill}
\sloppy\parskip0pt\relax
    \parshape2 \TitleParshapeLines
    \begingroup\centering\textbf{My Lipsum Title}\par\endgroup
    \parshape
    \ifnum\NbOfTextLines>20
          \NbOfTextLines
    \else 20\fi\space
    \TextParshapeLines
    \noindent\Text
    \par
%\vspace*{\fill}
\thispagestyle{empty}
\end{document}

For example:

enter image description here

(this example cuts short the text right after the mock parindent but I did not check what I was commenting out and now that image is uploaded I leave it like this, it would have been better to cut one line above or one line after in source rather than at this silly location)


To trigger the flood of upvotes, I have copied over the TikZ code as is from OP.

\documentclass{article}

\usepackage{tikz}
\usepackage[protrusion=false]{microtype}

\newcommand*{\TitleParshapeLines}{%
%    \parshape 2 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
}%

\newcommand*{\TopSemiCircleParshapeLines}{%
%    \parshape 9 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    0.00\hsize 1.00\hsize 
}

\newcommand*{\BotSemiCircleParshapeLines}{%
%    \parshape 11 
    0.00\hsize 1.00\hsize 
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
}

\newcommand{\Text}{%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  dolor lobortis lectus, in pellentesque ante est sit amet odio.
  Integer pretium iaculis sapien sit amet sagittis. Sed sagittis
  libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac
  enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id
  dictum. Nulla facilisi. Proin aliquet semper odio, sit amet eleifend
  ante.\newline
  \hspace*{\parindent}%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at
  congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  dolor lobor\dots
}

\newcommand*{\Radius}{1.75in}
\newcommand*{\Margins}{2pt}
\newcommand*{\ExtraLeftMargin}{0cm}% Set to 0.5cm if want to see line numbers


\newcommand\TextWidth{}
\edef\TextWidth{\the\dimexpr2\dimexpr\Radius}

\newcommand\PaperWidth{}
\edef\PaperWidth{\the\dimexpr\TextWidth+2\dimexpr\Margins}

\setbox0\vbox{\hsize\TextWidth\relax
  \sloppy\everypar{}\parskip0pt\relax
  \parshape
  21
  \TopSemiCircleParshapeLines
  \BotSemiCircleParshapeLines
  0pt \hsize
  \noindent\Text
  \par\xdef\NbOfTextLines{\the\prevgraf}%
}

\typeout{USING NBOFLINES = \NbOfTextLines}

%\newcommand*{\PaperHeight}{12.5cm}%<----- This is tweaked for this example

\newcommand\PaperHeight{}
% I assume here Title on 2 lines
\edef\PaperHeight{\the\dimexpr\numexpr2+
                      \ifnum\NbOfTextLines>20
                            \NbOfTextLines
                      \else 20\fi\relax\baselineskip}

\typeout{USING PAPERHEIGHT = \PaperHeight}

\usepackage[
    paperwidth=\dimexpr\PaperWidth+\ExtraLeftMargin\relax,
    paperheight=\PaperHeight,
    left=\dimexpr\Margins+\ExtraLeftMargin\relax,
    right=\Margins,
    top=\Margins,
    bottom=\Margins,
]{geometry}

\usepackage{xintkernel}

\edef\TextParshapeLines{%
  \TopSemiCircleParshapeLines
  \romannumeral\xintreplicate{\NbOfTextLines-20}{0pt \hsize}%
  \BotSemiCircleParshapeLines
}

%\show\TextParshapeLines

\tikzset{Frame Border Style/.style={thick, draw=red}}
\tikzset{Frame Fill Style/.style={fill=green!15}}
\tikzset{Frame Title Rule Style/.style={magenta, thin}}
\tikzset{Frame Title Fill Style/.style={fill=yellow!25}}

\newcommand*{\ExternalBorder}[1]{%
    \draw [#1]
        ([shift={(0.5*\ExtraLeftMargin-\Radius,-\Radius-\Margins)}]current page.north) 
        arc [start angle=180, end angle=0, radius=\Radius]
        -- ([shift={(0.5*\ExtraLeftMargin+\Radius,\Radius+\Margins)}]current page.south) 
        arc [start angle=0, end angle=-180, radius=\Radius]
        -- cycle
    ;
}%
\newcommand*{\TikzDecorations}[1]{%
    \noindent
    \begin{tikzpicture}[remember picture,overlay]
        \ExternalBorder{Frame Border Style, Frame Fill Style}
        \begin{scope}%% ------------------------------------ Title
            \clip 
                ([shift={(0.5*\ExtraLeftMargin,-\Radius-\Margins)}]current page.north) 
                    circle (\Radius);
            \draw [Frame Title Rule Style, Frame Title Fill Style] 
            ([shift={(-2*\Radius-\Margins+0.20*\TextWidth,-2.25*\baselineskip)}]current page.north east)
            rectangle ++(0.60*\TextWidth,2.25*\baselineskip-\Margins)
            ;
        \end{scope}
        \ExternalBorder{Frame Border Style}
    \end{tikzpicture}%
}

\begin{document}
%\topskip0pt
%\vspace*{\fill}
\sloppy\parskip0pt\relax
\TikzDecorations{My Lipsum Title}%
    \parshape2 \TitleParshapeLines
    \begingroup\centering\textbf{My Lipsum Title}\par\endgroup
    \parshape
    \ifnum\NbOfTextLines>20
          \NbOfTextLines
    \else 20\fi\space
    \TextParshapeLines
    \noindent\Text
    \par
%\vspace*{\fill}
\thispagestyle{empty}
\end{document}

Produces this:

enter image description here

Some adjustments are needed (it seems the Tikz borders are a bit narrow), but @PeterGrill will know better than me how to get the circles a bit wider.

Or, use slightly modified parshape parameters for the bottom circle, narrowing a bit. Maybe a simple modification in the included Tikz code, but easier for Peter to do than by myself.


Case of shorter text

enter image description here

With

\newcommand{\Text}{%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus.%  Vestibulum tristique
  % metus vitae sem sagittis gravida.
  % Aliquam commodo arcu quam, at
  % congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci
  % dolor lobortis lectus, in pellentesque ante est sit amet odio.
  % Integer pretium iaculis sapien sit amet sagittis. Sed sagittis
  % libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac
  % enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id
  % dictum. Nulla facilisi.
  Proin aliquet semper odio, sit amet eleifend
  ante.\newline
  \hspace*{\parindent}%
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem
  ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec
  tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia
  urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum
  hendrerit augue at libero dignissim cursus. Vestibulum tristique
  metus vitae sem sagittis gravida.%  Aliquam commodo arcu quam, at
  % congue ex commodo quis. 
  Ut dapibus, quam id suscipit vulputate, orci
  dolor lobor\dots
}

My feeling here is that either the parshape parameters need some adjustments, or the parameters of the Tikz drawing.

  • Wow!! This looks very promising. Weird that the \parshape parameters would need tweaking on the bottom only (as the bottom ones are just a reverse of the top semicircle \parshape). I think perhaps the number of lines setting needs tweaking. Will investigate further. Thanks. – Peter Grill Sep 15 '18 at 16:06
  • @PeterGrill the case of short text seems to indicate the \parshape parameters do need some adjustment, because here it is not matter of number of lines. (perhaps there was a shift by one line in those \parshape parameters ?) – user4686 Sep 15 '18 at 16:32
  • @PeterGrill by inserting \leftskip10pt\rightskip\leftskip before \parshape both in the \vbox and in the typesetting (after the \endgroup of the title), I obtain about satisfactory result. No collision with circle border. Sadly, I also hit then against a case where the last word hyphenated as lo-bor causes a bor on page 2... I feel the \parshape parameters need to be made shorter. – user4686 Sep 15 '18 at 17:06
  • \leftskip15pt\rightskip\leftskip at the two locations (measuring in the \vbox and typesetting again for real) appears not to have problems but text shape starts being a bit elliptical... – user4686 Sep 15 '18 at 17:12
6

Just for fun. I completely switched gears now and employ this very stunning answer by Paul Gaborit. One of its major advantages is that it makes use of the full functionality of the parshape package. (I did not really dare to touch the macros, even though I am tempted to replace all the pgfextra code by an arguably less dangerous syntax.) What is absolutely stunning about this macro is that it allows you to fill arbitrary shapes with texts, and the result looks good! (What I did not manage to achieve is to make use of \centering in conjunction with these advanced par shaping methods, and I do not know if that's possible or done.) The procedure below works as follows:

  1. Measure/estimate the dimensions of the text (along the lines of my comment and jfbu's nice answer). It is clear that this is just an estimate, which may be off.
  2. Draw the title shape using Paul Gaborit's stunning code and fill it with text.
  3. Draw the lower shape and fill it.

Here is the code.

\documentclass[border=3.14mm]{standalone}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{shapepar}
\usepackage{microtype}
\usepackage{lipsum}
\usepackage{expl3}
\ExplSyntaxOn% from https://tex.stackexchange.com/a/16192/121799
\cs_new_eq:NN \Repeat \prg_replicate:nn
\ExplSyntaxOff
\usepackage{tikz}
\usetikzlibrary{calc,fit,intersections}
% from one of the most stunning answers I have seen: https://tex.stackexchange.com/a/68740/121799
\def\shapeparnodeaccuracy{2}
\newcommand\shapeparnode[6][]{
  % 6 parameters:
  % style for node (default:empty),
  % h margin, v margin, left path, right path, text (just one paragraph!)

  % name left and right paths and compute there bounding boxes
  \begin{scope}[local bounding box=leftbb,xshift=#2] 
    \path[name path global=left] #4;
  \end{scope}
  \node[inner ysep=-#3,inner xsep=0pt,fit=(leftbb)](leftbb){};
  \begin{scope}[local bounding box=rightbb,xshift=-#2]
    \path[name path global=right] #5;
  \end{scope}
  \node[inner ysep=-#3,inner xsep=0pt,fit=(rightbb)](rightbb){};

  % global bounding box
  \path let
  \p1=(leftbb.north west), \p2=(leftbb.south west),
  \p3=(rightbb.north east), \p4=(rightbb.south east)
  in
  \pgfextra{
    \pgfmathsetmacro{\ymin}{(\y1 < \y3) ? \y1 : \y3}
    \pgfmathsetmacro{\ymax}{(\y2 > \y4) ? \y2 : \y4}
    \typeout{ymin \ymin}
    \typeout{ymax \ymax}
  } node[inner sep=0,fit={(\x1,\ymin pt)(\x3,\ymax pt)}](mybb){};

  % compute nb steps
  \path let \p1=(mybb.north), \p2=(mybb.south) in
  \pgfextra{
    \pgfmathsetmacro{\fnthght}{1em/\shapeparnodeaccuracy}
    \pgfmathtruncatemacro{\nbsteps}{(\y1-\y2)/\fnthght}
    \xdef\nbsteps{\nbsteps}
    \typeout{nb steps \nbsteps}
  };

  % horizontal references
  \path (mybb.north) -- (mybb.south)
  \foreach \cnt in {0,1,...,\nbsteps}{
    \pgfextra{\pgfmathsetmacro{\pos}{\cnt/\nbsteps}}
    coordinate[pos=\pos] (ref \cnt)
  };

  % left and right boundaries coordinates
  \foreach \cnt in {0,1,...,\nbsteps}{
    % an horizontal line from left to right
    \path[name path=ltor]
    (mybb.west |- ref \cnt) --  (mybb.east |- ref \cnt);
    % same line from right to left
    \path[name path=rtol]
    (mybb.east |- ref \cnt) -- (mybb.west |- ref \cnt);
    % left boundary
    \path[name intersections={of=rtol and left,by={l \cnt},sort by=rtol}];
    % right boundary
    \path[name intersections={of=ltor and right,by={r \cnt},sort by=ltor}];
  }
  % start point (and initial value of boundshape)
  \path let \p1=(l 0) in 
  \pgfextra{
    \pgfmathsetmacro{\xstart}{\x1}
    \xdef\boundshape{{0}{0}b{\xstart}}
    \xdef\xmin{\xstart}
    \xdef\xmax{\xstart}
  };

  % top and bottom
  \path let \p1=(l 0), \p2=(l \nbsteps) in
  \pgfextra{
    \pgfmathsetmacro{\ystart}{\y1}\xdef\ystart{\ystart}
    \pgfmathsetmacro{\yending}{\y2}\xdef\yending{\yending}
  };
  % incremental definition of boundshape
  \foreach \cnt in {0,1,...,\nbsteps}{
    \path let \p1=(l \cnt), \p2=(r \cnt) in
    \pgfextra{
      \pgfmathsetmacro{\start}{\x1}
      \pgfmathsetmacro{\len}{\x2-\x1}
      \pgfmathsetmacro{\ypos}{\cnt/\nbsteps*(\ystart - \yending)}
      {\let\\=\relax \xdef\boundshape{\boundshape\\{\ypos}t{\start}{\len}}}
      \pgfmathsetmacro{\xmin}{(\xmin < \start) ? \xmin : \start}
      \xdef\xmin{\xmin}
      \pgfmathsetmacro{\xmax}{(\xmax > \start + \len) ? \xmax : \start + \len}
      \xdef\xmax{\xmax}
    };
  }
  % draw the node with text in a shapepar
  \pgfmathsetmacro{\ymax}{\ystart - \yending}
  {\let\\=\relax \xdef\boundshape{\boundshape\\{\ymax}e{0}}}
  \node[#1,text width=\xmax pt - \xmin pt,align=flush left,
  anchor=north west,inner sep=0]
  at (mybb.north west -| \xmin pt,0)
  {\Shapepar[1pt]{\boundshape}#6\par};
}
\newcommand*{\Radius}{1.75in}

\newcommand*{\FullCircleParshape}[1][23]{%
    \pgfmathtruncatemacro{\X}{#1-21}
    \parshape #1 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    \ifnum\X>0 
    \Repeat{\X}{0.00\hsize 1.00\hsize}
    \fi
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
    0.00\hsize 1.00\hsize 
}
\pgfmathsetmacro{\myTextWidth}{(2*\Radius-2em)/1pt}
\newlength\TextWidth
\setlength\TextWidth{\myTextWidth pt}
\newlength\TotalHeight
\newlength\TitleHeight
\newsavebox\tmpbox
\newsavebox\titlebox
\newcommand{\EstimateDimensionsOfOvershootingParShape}[2]{%
\savebox\tmpbox{\parbox[b]{\TextWidth}{\FullCircleParshape[23]\begingroup%
\centering\textbf{#1}\newline #2\par%
\endgroup}}%
\setlength\TotalHeight{\ht\tmpbox}
\savebox\titlebox{\parbox[b]{3.5in}{\FullCircleParshape[23]\begingroup%
\centering\textbf{#1}\par\endgroup}}%
\setlength\TitleHeight{\ht\titlebox}
}


\def\mytext{Lorem ipsum dolor sit amet, consectetur adipiscing
  elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue
  ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis
  dolor. Praesent et diam eget libero egestas mattis sit amet vitae
  augue. Nam tincidunt congue enim, ut porta lorem lacinia
  consectetur. $x = y + z$ arcu vehicula ultricies a non tortor. Lorem
  ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida
  lorem. \textbf{Ut turpis felis}, pulvinar a semper sed, adipiscing id
  dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur
  dapibus enim sit amet elit pharetra tincidunt feugiat nisl
  imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed
  odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu
  rhoncus. \emph{\large Cum sociis natoque} penatibus et magnis dis
  parturient montes, nascetur ridiculus mus. In rutrum accumsan
  ultricies. Mauris vitae nisi at sem facilisis semper ac in
  est. Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut
  tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper
  ultricies.}
\def\mytitle{This here is a very long title!}

\begin{document}%
\EstimateDimensionsOfOvershootingParShape{\mytitle}{\mytext}
%\typeout{\the\TotalHeight,\the\TitleHeight}
\tikzset{Frame Border Style/.style={thick, draw=red}}
\tikzset{Frame Fill Style/.style={fill=green!15}}
\tikzset{Frame Title Rule Style/.style={magenta, thin}}
\tikzset{Frame Title Fill Style/.style={fill=yellow!25}}
\begin{tikzpicture}
      \pgfmathsetmacro{\myangle}{acos((\Radius-\the\TitleHeight-15pt)/\Radius)}
      \def\pathone{(0,-2pt) arc(90:90+\myangle:0.9*\Radius)}%
      \def\pathtwo{(0,-2pt) arc(90:90-\myangle:0.9*\Radius)}%
      \begin{scope}[local bounding box=top]
      \path (0,0) arc(90:90+\myangle:\Radius) coordinate (start1);
      \path (0,0) arc(90:90-\myangle:\Radius) coordinate (start2);
      \draw [Frame Title Rule Style,Frame Title Fill Style]
      (start2) -- (start1)  arc(90+\myangle:90-\myangle:\Radius);
      \shapeparnode[text=blue,font=\bfseries]
      {1em}{.5em}{\pathone}{\pathtwo}{\mytitle}%
      \end{scope}
      \pgfmathsetmacro{\ydist}{max(\TotalHeight-\TitleHeight-2*\Radius,0)}   
      \def\pathone{(start1) arc(90+\myangle:180:{0.9*\Radius} and \Radius) 
      -- ++(0,-\ydist*1pt) arc(180:270:{0.9*\Radius} and \Radius)}%
      \def\pathtwo{(start2) arc(90-\myangle:0:{0.9*\Radius} and \Radius) 
      -- ++(0,-\ydist*1pt) arc(0:-90:{0.9*\Radius} and \Radius)}%
      \begin{scope}[local bounding box=bottom]
      \draw[Frame Border Style,Frame Fill Style] (start1) arc(90+\myangle:180:\Radius) 
      coordinate (aux3)
       -- ++(0,-\ydist*1pt-12pt)  arc(180:360:\Radius) coordinate (aux4)
       -- (aux3-|aux4) arc(0:90-\myangle:\Radius) -- cycle;
      \shapeparnode[text=blue]
      {1em}{.5em}{\pathone}{\pathtwo}{\mytext}%
      \end{scope}
\end{tikzpicture}
\end{document}

enter image description here


OLDER ANSWER: I seem to have ended up using a similar strategy as jfbu, whose answer I see just now. The common problem seems to be that the way that parshape shapes paragraphs/text is not always great if used in that way. What this code does is to measure the height of the text and then draws a boundary around it. (No, there is no problem to shade the text, I just didn't do it. It can be done by filling the shape and using an appropriate blend mode, or by placing an appropriate savebox in the .) One aspect in which my answer is definitely weaker than jfbu's is how it deals with too little text. My code does not deal with that at all. The reason why I didn't deal with that is that I would like to know what to do in that case. Personally I would just use a smaller size circle in that case (rather than a partly filled big circle.) Another difference is that I use the tikzmark library. This helps to avoid having the text going out of bounds. This can definitely be improved.

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn% from https://tex.stackexchange.com/a/16192/121799
\cs_new_eq:NN \Repeat \prg_replicate:nn
\ExplSyntaxOff
\usepackage{lipsum}
\usepackage{tikz}
\usetikzlibrary{tikzmark,intersections}
\usepackage[protrusion=false]{microtype}


\usepackage[mathlines]{lineno}
\linenumbers

\newcommand*{\Radius}{1.75in}
\newcommand*{\Margins}{2pt}
\newcommand*{\ExtraLeftMargin}{0cm}% Set to 0.5cm if want to see line numbers

\newcommand*{\PaperHeight}{12.5cm}%<----- This is tweaked for this example

\newlength{\TextWidth}
\newlength{\PaperWidth}
\pgfmathsetlength{\TextWidth}{2*\Radius}
\pgfmathsetlength{\PaperWidth}{\TextWidth+2*\Margins}

\usepackage[
    paperwidth=\dimexpr\PaperWidth+\ExtraLeftMargin\relax,
    paperheight=\PaperHeight,
    left=\dimexpr\Margins+\ExtraLeftMargin\relax,
    right=\Margins,
    top=\Margins,
    bottom=\Margins,
]{geometry}

\tikzset{Frame Border Style/.style={thick, draw=red}}
\tikzset{Frame Fill Style/.style={fill=green!15}}
\tikzset{Frame Title Rule Style/.style={magenta, thin}}
\tikzset{Frame Title Fill Style/.style={fill=yellow!25}}


\newcommand*{\FullCircleParshape}[1][23]{%
    \pgfmathtruncatemacro{\X}{#1-21}
    \parshape #1 
    0.40\hsize 0.20\hsize
    0.26\hsize 0.48\hsize 
    0.20\hsize 0.60\hsize 
    0.14\hsize 0.72\hsize 
    0.10\hsize 0.80\hsize 
    0.07\hsize 0.86\hsize 
    0.05\hsize 0.90\hsize 
    0.03\hsize 0.94\hsize 
    0.025\hsize 0.95\hsize 
    0.01\hsize 0.99\hsize 
    \ifnum\X>0 
    \Repeat{\X}{0.00\hsize 1.00\hsize}
    \fi
    0.01\hsize 0.99\hsize 
    0.025\hsize 0.95\hsize 
    0.03\hsize 0.94\hsize 
    0.05\hsize 0.90\hsize 
    0.07\hsize 0.86\hsize 
    0.10\hsize 0.80\hsize 
    0.14\hsize 0.72\hsize 
    0.20\hsize 0.60\hsize 
    0.26\hsize 0.48\hsize 
    0.40\hsize 0.20\hsize
    0.00\hsize 1.00\hsize 
}

\newcommand{\TextA}{%
Koalae et marmottae et duckae, lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum hendrerit augue at libero dignissim cursus. Vestibulum tristique metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci dolor lobortis lectus, in pellentesque ante est sit amet odio. Integer pretium iaculis sapien sit amet sagittis. Sed sagittis libero libero, eu tempus neque malesuada quis. Aliquam id mauris ac enim tincidunt aliquet nec sit amet odio. In ultricies in nulla id dictum. Nulla facilisi. Proin aliquet semper odio, sit amet eleifend ante.}

\newcommand{\TextB}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sem ante, efficitur eget diam quis, lacinia sollicitudin elit. Donec tempor sodales finibus. Fusce accumsan laoreet lorem, eget lacinia urna. Phasellus ornare nibh quis metus dignissim aliquam. Vestibulum hendrerit augue at libero dignissim cursus. Vestibulum tristique metus vitae sem sagittis gravida. Aliquam commodo arcu quam, at congue ex commodo quis. Ut dapibus, quam id suscipit vulputate, orci dolor lobor\dots}

\newlength\TotalHeight
\newlength\NewHeight
\newsavebox\tmpbox
\newcounter{obox}
\newcommand{\OvershootingParShape}[2]{%
\stepcounter{obox}
\savebox\tmpbox{\parbox[b]{\textwidth}{\FullCircleParshape[23]\begingroup%
\centering\textbf{#1}\newline #2\par%
\endgroup}}%
\setlength\TotalHeight{\ht\tmpbox}
\pgfmathtruncatemacro{\myl}{25+(\TotalHeight-258)/12}
\typeout{\myl}
\savebox\tmpbox{\parbox[b]{\textwidth}{\FullCircleParshape[\myl]\begingroup%
\centering\textbf{#1}\newline #2\par%
\endgroup}}%
\setlength\NewHeight{\ht\tmpbox}
\typeout{\the\TotalHeight -> \the\NewHeight}
\ifnum\myl<21
\typeout{Too\space little\space text.\space
Macro\space is\space not\space (yet)\space made\space for\space that.}
\fi
\FullCircleParshape[\myl]\begingroup%
\centering\textbf{\tikzmark{top-\theobox}#1}\newline \tikzmark{tbottom-\theobox}#2\par%
\endgroup
\begin{tikzpicture}[overlay,remember picture]
\pgfmathsetmacro{\myheight}{12*(\myl-24)*1pt+258*1pt}
\path ([yshift=12pt]pic cs:top-\theobox) coordinate (top) 
([yshift=-\myheight-24pt]top) coordinate (bottom) 
([yshift=+9pt]pic cs:tbottom-\theobox) coordinate (tbottom);
\draw[Frame Border Style,name path=oval] (current page.center |-top)  arc(90:0:\Radius) 
coordinate (aux1)
-- ([yshift=\Radius]bottom -|aux1) arc(0:-180:\Radius) coordinate(aux2)
--  ([yshift=-\Radius]top -|aux2) arc(180:90:\Radius) -- cycle;
\path[name path=hori] (aux1 |- tbottom) -- (aux2 |- tbottom);
\draw[Frame Border Style,name intersections={of=oval and hori,by={aux3,aux4}}]
(aux3) -- (aux4);
\end{tikzpicture}}
\begin{document}%
\OvershootingParShape{This is a title of arbitrary length!}{\TextA}
\newpage
\OvershootingParShape{This is a title of arbitrary length!}{\TextB\TextB}
\end{document}

enter image description here

enter image description here

  • There revised answer based on `\shapeparnode has issues when the text is small enough to fit into a circle. – Peter Grill Sep 16 '18 at 3:12
  • @PeterGrill Yes, you're right. Was easy to fix, though, just need to make sure that \ydist does not become negative: \pgfmathsetmacro{\ydist}{max(\TotalHeight-\TitleHeight-2*\Radius,0)}. – user121799 Sep 16 '18 at 4:19

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.