顶部和底部为半圆形的盒子

顶部和底部为半圆形的盒子

我正在尝试设计一个顶部和底部具有半圆形截面的盒子,如下所示:

在此处输入图片描述

如果文本足够小,则旨在缩小到给定的圆圈\Radius(而不是更小的圆圈):

在此处输入图片描述

理想的解决方案是如果tcolorbox可以用来产生这个,但是在文档中没有看到有关圆形的任何内容\parshape

笔记:

生成上面第一幅图像的代码如下平均能量损失,但也存在一些问题,包括:

  • 底部\parshape已手动应用到正确的位置。
  • \PaperHeight通过反复试验来确定。目标是使高度增加(或减少到)2\Radius以容纳所有内容这样的框(即没有多页问题)。
  • 标题应能容纳 2 行。如果超过 2 行,则无法正常工作。

参考:

代码:

\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

为了简化操作,我删除了所有 TikZ 装饰,但您可以恢复它们。我还重命名了一些宏,并删除了\parshape一些宏以方便重复使用。

还要注意,现在\TextA\TextB已合并到\Text(这是客观的),但其中的段落是用模拟的\newline,以不取消当前的 parshape。

整个代码旨在产生一个单一的 parshape:顶部圆、中间区域、底部圆。

\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}

给予

在此处输入图片描述

这是编辑过的代码,兼容较短的文本:

\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}

例如:

在此处输入图片描述

(这个例子在模拟 parindent 之后截断了文本,但我没有检查我注释掉了什么,现在图像已经上传,我把它保留成这样,最好是在源代码中剪切上面一行或后面一行,而不是在这个愚蠢的位置)


为了引发大量的点赞,我从 OP 中原封不动地复制了 TikZ 代码。

\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}

产生这个:

在此处输入图片描述

需要进行一些调整(似乎 Tikz 边框有点窄),但 @PeterGrill 比我更了解如何让圆圈变得更宽一些。

或者,对底部圆使用稍微修改过的 parshape 参数,稍微缩小一点。也许在包含的 Tikz 代码中进行简单的修改,但对 Peter 来说比我自己做更容易。


较短文本的情况

在此处输入图片描述

\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
}

我的感觉是,要么 parshape 参数需要进行一些调整,要么 Tikz 绘图的参数需要进行一些调整。

答案2

只是为了好玩。我现在完全改变了思路,开始使用Paul Gaborit 的回答非常令人震惊。它的一个主要优点是它充分利用了 parshape 包的全部功能。(我不敢触碰宏,尽管我很想用一种可能不那么危险的语法替换所有的 pgfextra 代码。)这个宏绝对令人惊叹的是它允许你用文本填充任意形状,而且结果看起来不错!(我没有做到的是与\centering这些高级 par 整形方法结合使用,我不知道这是否可能或已经完成。)下面的过程如下:

  1. 测量/估计文本的尺寸(类似于我的评论jfbu 的回答很好)显然这只是一个估计,可能会有偏差。
  2. 使用 Paul Gaborit 的惊人代码绘制标题形状并用文本填充。
  3. 绘制下方形状并填充。

这是代码。

\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}

在此处输入图片描述


旧答案:我似乎最终使用了与 jfbu 类似的策略,我刚刚看到了他的答案。常见的问题似乎是,如果以这种方式使用,parshape 塑造段落/文本的方式并不总是很好。此代码的作用是测量文本的高度,然后在其周围绘制边界。(不,给文本加阴影没有问题,我只是没有这样做。可以通过填充形状并使用适当的混合模式,或者在 中放置适当的保存框来完成。)我的答案肯定比 jfbu 的答案弱的一个方面是它如何处理太少的文本。我的代码根本没有处理这个问题。我没有处理这个问题的原因是我想知道在这种情况下该怎么做。就我个人而言,在这种情况下,我只会使用较小尺寸的圆圈(而不是部分填充的大圆圈)。另一个区别是我使用了库tikzmark。这有助于避免文本超出范围。这肯定可以改进。

\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}

在此处输入图片描述

在此处输入图片描述

相关内容