给定几个段落的内容(肯定有最后一个环境发生{MyList}
),我想定义一个宏,可以把这个内容分解成内容前第 n 项的结尾{MyList}
和内容后第 n 个项目。
也就是说,给定以下形式的输入
Some text before
\begin{MyList}
\item First.
\item Second.
\item Third.
\end{MyList}
Some text after
将其转换为(假设 n=1)
Some text before
\begin{MyList}
\item First.
\end{MyList}% end list after n items
%% -----------
\begin{MyList}% restart list after n items
\item Second.
\item Third.
\end{MyList}
Some text after
并对两部分分别进行处理。
如果 n=2,则所需的变换为
Some text before
\begin{MyList}
\item First.
\item Second.
\end{MyList}% end list after n items
%% -----------
\begin{MyList}% restart list after n items
\item Third.
\end{MyList}
Some text after
理想情况下,我希望提供一个整数来指示要中断的行项数。但是,如果没有足够的 TeX magic 来执行此操作,则一个选项是要求\MarkBoundaryPoint
将一个放置在列表中以指示要发生中断的点。
这包含在 MWE 中,但目前是 NOOP。
如果仍然不可能,那么可以实现这一目标的最低标记是多少?
笔记:
- 我正在使用始终恢复的列表所以编号不是问题。
\IncludeUpToFirstNItemsOfMyList
对和进行适当修改后,MWE 的期望输出\IncludeAfterFirstNItemsOfMyList
将是:
代码:
\documentclass{article}
\usepackage{enumitem}
%% This list is ALWAYS resumed.
%% https://tex.stackexchange.com/questions/366179/always-resumed-list-does-not-always-resume-if-invoked-from-within-an-environment
\newlist{MyList}{enumerate}{2}
\setlist[MyList,1]{
label=\arabic*.,
before=\setcounter{MyListi}{\value{MyList}},
after=\setcounter{MyList}{\value{MyListi}},
}% ALWAYS resumed
\setlist[MyList,2]{label=\alph*)}
\newcounter{MyList}
\newcommand{\IncludeUpToFirstNItemsOfMyList}[2][1]{%
%% #1 = number of list item to put in fbox
%% #2 = content which includes a {MyList}
%%
%% ??? How do I ignore ALL text AFTER first n items?
#2% TBD: This macro should cutoff any text AFTER item number specified in #1
}%
\newcommand{\IncludeAfterFirstNItemsOfMyList}[2][1]{%
%% #1 = number of list item to discard
%% #2 = content which includes a {MyList}
%%
%% ??? How do I ignore ALL text up to the first n items?
#2% TBD: This macro should cutoff any text BEFORE item number specified in #1
}%
\newcommand*{\MarkBoundaryPoint}{}% 2nd option (in case it is not possible to do without)
\newcommand{\PutPartOfListInFbox}[2][1]{%
%% #1 = number of list item to put in fbox
%% #2 = content which includes a {MyList}
%%
\noindent
\fboxsep=0pt
\fbox{%
%% This is to test this capability and display it to be able to see that it
%% worked as desired.
\begin{minipage}{0.5\linewidth}
\IncludeUpToFirstNItemsOfMyList[#1]{#2}%
\end{minipage}%
}%
\IncludeAfterFirstNItemsOfMyList[#1]{#2}%
}
\newcommand*{\MyContent}{%
Some optional content before the list.
\begin{MyList}
\item First Item of list. This will be nonempty, but may or may not have more than
one paragraph.
Second optional para of first Item.\MarkBoundaryPoint
\item Second Item is optional.
Could be zero (in which case this item would not exist)
or more paragraphs.
Second optional para of second Item.
\item Third Item is optional.
Second optional para of third Item.
\end{MyList}%
Some optional content after the list.
}%
\begin{document}
\PutPartOfListInFbox{\MyContent}
\end{document}
答案1
以下是一种方法。
- 每次 之前
\item
,我们都会检查 的当前值MyListi
。如果它等于某个阈值(通过 给出)\MyListSeparateAfter
,我们就在那里分割列表。 - 可以在两个列表部分之间插入一些内容,通过 给出
\MyListInsertBetween
。 Boxed
在其内容周围绘制一个框。您可以指定框的所需宽度(\linewidth
默认)以及\fbox
参数。MyListBoxed
是否符合您的要求(我认为)。如您的示例所示,我绘制的框没有填充,但我认为应该添加一些。- 根据您的应用程序,某些
\hrules
(或类似)可能比方框更好。这将允许分页,而不会引入任何复杂的水平间距问题。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{enumitem}
\makeatletter
\newlist{MyList}{enumerate}{2}
% We want out list to always resume, so we store the current list, so we store
% the current value in the counter MyList at the end of each list and retreave
% it at the beginning.
% We also add a hook to the start of \item which can be used to split the list.
\setlist[MyList,1]{
label=\arabic*.,
before={%
\setcounter{MyListi}{\value{MyList}}%
\preto\item{\MyListSeparate@maybe}%
},
after=\setcounter{MyList}{\value{MyListi}},
}
\setlist[MyList,2]{label=\alph*)}
\newcounter{MyList}
% If we are at level 1 of the list and the value of the counter has reached
% the threshold, we remove the threshold and insert \MyListSeparate.
\newcommand*\MyListSeparate@maybe{%
\ifnum\enit@depth=1\relax
\ifnum\MyList@separate@threshold=\value{MyListi}\relax
\MyListSeparateAfter{\MyList@separate@threshold@false}%
\expandafter\expandafter
\expandafter\MyListSeparate
\fi
\fi
}
% This command separates the list.
\newcommand*\MyListSeparate{%
\end{MyList}%
\MyList@between
\begin{MyList}%
}
% Command for setting the threshold.
\newcommand*\MyListSeparateAfter[1]{%
\xdef\MyList@separate@threshold{#1}%
}
% The threshold will be used in an \ifnum as the first number to compare. This
% default value makes the comparison false regardless of the second number.
\def\MyList@separate@threshold@false{0<0}
\MyListSeparateAfter{\MyList@separate@threshold@false}
% Command for setting the code to insert between the list parts.
\newcommand\MyListInsertBetween[1]{%
\def\MyList@between{#1}%
}
\MyListInsertBetween{}
% A wrapper to capture and box any content (does not allow page breaks).
\NewDocumentEnvironment{Boxed}{
D||{\linewidth} % width of the \fbox
o % value for \fboxsep
o % value for \fboxrule
}{%
% Adjust the \fbox parameters.
\IfValueT{#2}{%
\fboxsep=#2\relax
}
\IfValueT{#3}{%
\fboxrule=#3\relax
}
\par\noindent
% Start a box to save the environment content in. It has to be narrower than
% \linewidth in order to account for the \fbox that will be added afterwards.
\setbox\@tempboxa=\hbox\bgroup
\begin{minipage}[t]{\dimexpr#1-2\fboxsep-2\fboxrule\relax}%
}{%
\end{minipage}%
\egroup
\fbox{\usebox\@tempboxa}%
\par
}
% An environment that automatically splits the list after a given item and
% puts the first part in a box. If no threshold or a too large one is given,
% the whole list is boxed. If a too small threshold is given, no box is drawn.
\NewDocumentEnvironment{MyListBoxed}{
O{\MyList@separate@threshold@false} % The splitting threshold
+O{} % Code to insert between the box and the second list part.
+D||{} % Code to insert before the list, inside the box.
}{%
% Store the argument values in the appropriate hooks.
\MyListSeparateAfter{#1}%
\MyListInsertBetween{#2}%
% Check if the threshold is too small.
\ifnum\MyList@separate@threshold<\numexpr\value{MyList}+1\relax
% If so, remove the threshold, insert the before-text and start the list.
\MyListSeparateAfter{\MyList@separate@threshold@false}%
#3%
\begin{MyList}%
\else
% If not, start a Boxed environment, insert the before-text and start the list.
\begin{Boxed}[0pt]%
\def\@tempa{\end{Boxed}}%
#3%
\begin{MyList}%
% Now we tell TeX to insert \@tempa after the end of the current group,
% which is at \end{MyList}. If the threshold is not met within the list,
% it is the one inserted by \end{MyListBoxed}.
\aftergroup\@tempa
\fi
}{%
\end{MyList}%
}
\makeatother
\begin{document}
We can do it manually:
\MyListSeparateAfter{3}
\MyListInsertBetween{Hello}
\begin{MyList}
\item one
\item two
\item three
\item four
\item five
\end{MyList}
Or we can automate it somewhat:
\begin{MyListBoxed}[7]|Some optional content before the list.|
\item First Item of list. This will be nonempty, but may or may not have more than
one paragraph.
Second optional para of first Item.
\item Second Item is optional.
Could be zero (in which case this item would not exist)
or more paragraphs.
Second optional para of second Item.
\begin{MyList}
\item foo
\item bar
\end{MyList}
\item Third Item is optional.
Second optional para of third Item.
\item Four
\begin{MyList}
\item tic
\item toc
\end{MyList}
\end{MyListBoxed}
Some optional content after the list.
\end{document}
如果您也想将列表的第二部分装箱,就像您在评论中所写的那样,您可以轻松地调整定义来MyListBoxed
做到这一点。
% An environment that automatically splits the list after a given item and
% puts each part in an \fbox. If the threshold is not given or not in the
% range of the list, the whole list is boxed (and the between-text thrown
% away).
\NewDocumentEnvironment{MyListBoxed}{
O{\MyList@separate@threshold@false} % The splitting threshold
+D||{} % Code to insert before the list, inside the first box.
+O{} % Code to insert between the boxes.
+D||{} % Code to insert after the list, inside the second box.
}{%
% Store the argument values in the appropriate hooks.
\MyListSeparateAfter{#1}%
\MyListInsertBetween{\end{Boxed}#3\begin{Boxed}}%
\def\@tempa{#4}%
% Check if the threshold is the current counter value.
\ifnum\MyList@separate@threshold=\numexpr\value{MyList}\relax
% If so, remove the threshold.
\MyListSeparateAfter{\MyList@separate@threshold@false}%
\fi
% Start a Boxed environment, insert the before-text and start the list.
\begin{Boxed}[0pt]%
#2%
\begin{MyList}%
}{%
\end{MyList}%
\@tempa
\end{Boxed}%
}
请注意,我稍微改变了一些论点。
\begin{MyListBoxed}[7]
|Some optional content before the lists.|
[Some optional content between the lists.]
|Some optional content after the lists.|
<list \items>
\end{MyListBoxed}