将框对齐到左边距,同时保持 \parindent 粘合/服从 \noindent(如果存在)

将框对齐到左边距,同时保持 \parindent 粘合/服从 \noindent(如果存在)

如何定义一个接受两个参数的命令,并根据第一个参数创建一个新段落,其中包含第二个参数的框的右边距与该段落的左边距对齐,并且该框的参考点垂直位于该段落第一行文本的基线?

如果我做

\long\def\Command#1#2{\leavevmode\llap{#2}#1}

\Command{The text of the paragraph.}{Left justified?}

\Command{\noindent The text of the paragraph.}{Left justified?}

\bye

,则(正如预期的那样)\noindent没有任何效果,并且包含短语“左对齐?”的框的右边距没有与段落左边距对齐,而是与字母的左边距对齐T

如果不执行\leavevmodeI do \noindent,则包含短语“左对齐?”的框的右边距将与段落左边距和字母左边对齐,但字母前T不会出现-glue ,除非您“手动”将其添加为 的第一个参数的一部分:\parindentT\Command

\long\def\Command#1#2{\noindent\llap{#2}#1}

\Command{The text of the paragraph.}{Left justified?}

\Command{\noindent The text of the paragraph.}{Left justified?}

\bye

因此,在花了几天的时间解析 TeXbook 中难以理解的模棱两可的短语之后,我确实只找到了无法实现我想要的方法,并且我只知道了为什么我的尝试没有奏效。

有人能指出一种方法工作?

答案1

在此处输入图片描述

我认为您的意思是这样的,但不是完全清楚:

\long\def\Command#1#2{%
\everypar{\setbox0\lastbox\llap{#2}\box0\everypar{}}#1}

\Command{The text of the paragraph.}{Left justified?}

\Command{\noindent The text of the paragraph.}{Left justified?}

\bye

答案2

我接受了你的评论

差不多了。谢谢。\Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?}不行……

大卫·卡莱尔的回答考虑到:

\parindent水平模式开始时宽度的水平框生成并设置,无需进行钩选\everyhbox

因此,您可以创建一个机制,用于\if...\everyhbox/\everyvbox之前触发时设置 -switch \everypar

\Command根据第一个参数创建一个临时的垂直框。

在创建临时垂直框时,将宏标记添加到全局设置 -switch 的\everyhbox钩子上。将宏标记 添加到每个钩子上 ,并添加一条指令以恢复所有这些钩子。\everyvbox\if...
\everyhbox\everyvbox\everypar

影响:

如果在或\everypar之前执行,则钩子将被恢复,因此更改附加到/ 的-switch 的指令将消失并且永远不会被执行。\everyvbox\everyhbox\if..\everyhbox\everyvbox

如果\everyvbox或在\everyhbox之前执行\everypar,则设置 -switch 的指令\if将执行一次,并且所有钩子都会恢复。

由于涉及三个钩子(,,\everypar) ,我定义了一个递归例程,您可以在其中提供一个元组列表,第一个组件表示一个钩子,第二个组件表示此钩子在恢复元组列表中的所有钩子并传递钩子在重新定义之前传递的标记之前要传递的标记。\everyvbox\everyhbox\prependtorestorehooks

您还需要采取一些预防措施,以防机制/\Command嵌套。

注意事项:

  • 仅当钩子本身不包含\outer-token 时,附加到钩子/恢复钩子才有效。例如,以下代码会产生错误消息! Forbidden control sequence found while scanning text of \everypar.

    \begingroup
    \everypar={\problem}%
    \outer\def\problem{How to overcome the problem?}%
    \everypar=\expandafter{\the\everypar Again: How to overcome the problem?}%
    \endgroup
    \bye
    
  • \everyhbox/在创建/\everyvbox时触发,无论该框是否进入输出文件或进入框寄存器(其内容永远不会进入输出文件)。因此,可以通过在的参数开头放置 -assignment等来欺骗该机制。\hbox\vbox\setbox\Command

  • \vbox从 的第一个参数创建一个临时文件,用于在之前触发/ 的情况下\Command设置-switch 。因此, 的第一个参数的材料被处理了两次:一次用于创建临时框。一次用于创建应该进入输出文件的段落。可能有些事情你不想重复做。不幸的是,没有“获取框属性”/“框测量”模式可以关闭这些事情。\if...\everyhbox\everyvbox\everypar\Command\immediate\write

 

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
%\prependtorestorehooks{%
%  {{hook 1}{prepend 1}}%
%  {{hook 2}{prepend 2}}%
%  ...
%}%
\long\def\prependtorestorehooks#1{%
  \prependtorestorehookscreraterestore{}{#1}#1\relax
}%
\long\def\prependtorestorehookscreraterestore#1#2#3{%
  % #1 - Restore-List created so far
  % #2 - entire list
  % #3 - next element
  \ifx\relax#3\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\prependtorestorehooksprependtohooks{}{#1}#2\relax}%
  {%
    \prependtorestorehooksextract#3{#1}{#2}%
  }%
}%
\long\def\prependtorestorehooksextract#1#2{%
  \expandafter\prependtorestorehooksextractinner\expandafter{\the#1}{#1}%
}%
\long\def\prependtorestorehooksextractinner#1#2#3{%
  \prependtorestorehookscreraterestore{#3#2={#1}}%
}%
\long\def\prependtorestorehooksprependtohooks#1#2#3{%
  %#1 prepend-list-created so far
  %#2 restore-list
  %#3 {hook}{prepend}
  \ifx\relax#3\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {#1}{%
     \prependtorestorehooksprependtohooksb#3{#1}{#2}%
  }%
}%
\long\def\prependtorestorehooksprependtohooksb#1{%
  \expandafter\prependtorestorehooksprependtohooksc\expandafter{\the#1}{#1}%
}%
\long\def\prependtorestorehooksprependtohooksc#1#2#3#4#5{%
  \prependtorestorehooksprependtohooks{#4#2={#3#5#1}}{#5}%
}%
%------------------------------------------------------------------------------------

\newbox\MyBox
\newif\ifleadingbox
\newif\ifintestbox\intestboxfalse
\newif\ifhookdone\hookdonefalse
\def\firstindent{}%
\long\def\setifleadingboxandfirstindent#1{%
  \begingroup
  \ifintestbox\else\global\hookdonefalse\global\leadingboxfalse\fi
  \setbox\MyBox=\vbox{%
    \intestboxtrue
    \prependtorestorehooks{%
     {{\everypar}{\ifhookdone\else\setbox\MyBox=\lastbox\xdef\firstindent{\the\wd\MyBox}\box\MyBox\global\hookdonetrue\fi}}%
     {{\everyhbox}{\ifhookdone\else\global\leadingboxtrue\global\hookdonetrue\fi}}%
     {{\everyvbox}{\ifhookdone\else\global\leadingboxtrue\global\hookdonetrue\fi}}%
    }%
    #1%
  }%
  \endgroup
}%
\long\def\Command#1#2{%
  \par
  \setifleadingboxandfirstindent{#1}%
  \prependtorestorehooks{{{\everypar}{\setbox\MyBox\lastbox\llap{#2}\box\MyBox}}}%
  \ifleadingbox\leavevmode\else\noindent\hbox to\firstindent{\hfill}\fi#1%
}%

\leavevmode

\kern-1in

\noindent test \hfill test \hfill test

\setifleadingboxandfirstindent{a}%
\ifleadingbox some \else no \fi leading h-or vbox.
\ifleadingbox\else Paragraph is indented by: \firstindent\fi

\smallskip\hrule\smallskip

\setifleadingboxandfirstindent{\hbox{a}}%
\ifleadingbox some \else no \fi leading h-or vbox.
\ifleadingbox\else Paragraph is indented by: \firstindent\fi

\smallskip\hrule\smallskip

\setifleadingboxandfirstindent{\vbox{a}}%
\ifleadingbox some \else no \fi leading h-or vbox.
\ifleadingbox\else Paragraph is indented by: \firstindent\fi

\smallskip\hrule\smallskip

\setifleadingboxandfirstindent{\noindent\vbox{a}}%
\ifleadingbox some \else no \fi leading h-or vbox.
\ifleadingbox\else Paragraph is indented by: \firstindent\fi

\smallskip\hrule\smallskip

\noindent {\bf Test 1:}

\Command{The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 2:}

\Command{\noindent The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 3:}

\Command{\hbox to 5cm{The\hfill wide\hfill text} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 4:}

\Command{\noindent\hbox to 5cm{The\hfill wide\hfill text} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 5:}

\Command{\vtop{\hbox to 5cm{The\hfill wide\hfill text}\hbox to 5cm{The\hfill wide\hfill text}} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 6:}

\Command{\noindent\vtop{\hbox to 5cm{The\hfill wide\hfill text}\hbox to 5cm{The\hfill wide\hfill text}} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 7:}

\Command{\vbox{\hbox to 5cm{The\hfill wide\hfill text}\hbox to 5cm{The\hfill wide\hfill text}} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 8:}

\Command{\noindent\vbox{\hbox to 5cm{The\hfill wide\hfill text}\hbox to 5cm{The\hfill wide\hfill text}} of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 9:}

\newdimen\MyDimen
\setbox\MyBox=\hbox{Left justified?}
\MyDimen=\wd\MyBox

\Command{\noindent\kern\MyDimen The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 10:}

\Command{\leavevmode\kern\MyDimen The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 11:}

\Command{\noindent\kern\MyDimen \hbox{\vbox{\advance\hsize-\MyDimen\Command{\noindent The text of the paragraph.}{Left justified?}}}}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 12:}

\Command{\noindent\kern\MyDimen \hbox{\vbox{\advance\hsize-\MyDimen\Command{The text of the paragraph.}{Left justified?}}}}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 13:}

\Command{\leavevmode\kern\MyDimen \hbox{\vbox{\advance\hsize-\MyDimen\advance\hsize-\parindent\Command{\noindent The text of the paragraph.}{Left justified?}}}}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 14:}

\Command{\leavevmode\kern\MyDimen \hbox{\vbox{\advance\hsize-\MyDimen\advance\hsize-\parindent\Command{The text of the paragraph.}{Left justified?}}}}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 15:}

\Command{\kern\MyDimen The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 16:}

% `\Command` starts with `\par`, i.e., by resorting to (restricted) vertical mode. Thus \kerns at the beginning of \Command's first argument in any case are vertical.

\Command{\kern\MyDimen\noindent The text of the paragraph.}{Left justified?}

\smallskip\hrule\smallskip

\noindent {\bf Test 17:}

% `\Command` starts with `\par`, i.e., by resorting to (restricted) vertical mode. Thus \kerns at the beginning of \Command's first argument in any case are vertical.

\Command{\kern\MyDimen\hbox{T}he text of the paragraph.}{Left justified?}

\smallskip\hrule\bigskip

\noindent {\bf Test 18:}

\Command{The text of the paragraph. \par  The text of the paragraph.  \par  The text of the paragraph.\Command{\noindent The text of the paragraph. \par  The text of the paragraph. \par  The text of the paragraph.}{Left justified?}}{Left justified?}


\smallskip\hrule\bigskip

\noindent{\bf !!!! But: !!!}

\bigskip

\noindent {\bf Test 19:}

\Command{\setbox\MyBox=\hbox{Something to trigger the every-hook}\noindent This text is indented although it should not be indented. The reason is the triggering of {\tt\string\everyhbox} by {\tt\string\setbox...} right at the beginning of {\tt\string\Command}'s first argument.}{Left justified?}

\bye

在此处输入图片描述

说实话:

我不知道为什么需要这些。手册上\Command只说 TeX 在处理前切换到垂直模式#1。用户可以自行决定是否\leavevmode需要\noindent切换到水平模式。

相关内容