![Выровняйте поле по левому краю, сохраняя \parindent glue/соблюдая \noindent, если он есть](https://rvso.com/image/420064/%D0%92%D1%8B%D1%80%D0%BE%D0%B2%D0%BD%D1%8F%D0%B9%D1%82%D0%B5%20%D0%BF%D0%BE%D0%BB%D0%B5%20%D0%BF%D0%BE%20%D0%BB%D0%B5%D0%B2%D0%BE%D0%BC%D1%83%20%D0%BA%D1%80%D0%B0%D1%8E%2C%20%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D1%8F%D1%8F%20%5Cparindent%20glue%2F%D1%81%D0%BE%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0%D1%8F%20%5Cnoindent%2C%20%D0%B5%D1%81%D0%BB%D0%B8%20%D0%BE%D0%BD%20%D0%B5%D1%81%D1%82%D1%8C.png)
Как определить команду, которая принимает два аргумента и из первого аргумента создает новый абзац, в котором правое поле блока, содержащего второй аргумент, выравнивается по левому полю этого абзаца, а точка отсчета этого блока по вертикали находится на базовой линии первой строки текста этого абзаца?
Если я сделаю
\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
.
Если вместо \leavevmode
I do \noindent
, то правое поле поля, содержащего фразу "Выровнено по левому краю?", будет выровнено как по левому краю абзаца, так и по левому краю буквы, но перед буквой T
не будет -glue , если только вы не добавите его "вручную" как часть первого аргумента :\parindent
T
\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
решение2
Я принял ваш комментарий.
Почти. Спасибо.
\Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?}
Не получается...
наОтвет Дэвида Карлайлав учетную запись:
Горизонтальный ящик ширины \parindent
в начале горизонтального режима изготавливается и устанавливается без выноса крючка \everyhbox
.
Таким образом, можно создать механизм установки \if...
-переключателя в случае, если \everyhbox
/ \everyvbox
сработает раньше \everypar
:
Создайте временный вертикальный блок из \Command
первого аргумента.
При создании этого временного вертикального блока добавьте к хукам \everyhbox
/ \everyvbox
макротокен, который глобально устанавливает -switch \if...
.
Добавьте к каждому из хуков \everyhbox
/ \everyvbox
и \everypar
директиву для восстановления всех этих хуков.
Эффект:
В случае, если \everypar
выполняется до \everyvbox
или \everyhbox
, хуки будут восстановлены, и, таким образом, директива об изменении \if..
-switch, добавленная к \everyhbox
/ \everyvbox
, исчезнет и никогда не будет выполнена.
В случае , если \everyvbox
или \everyhbox
выполняется до \everypar
, директива по установке \if
-переключателя выполняется один раз и все хуки восстанавливаются.
Поскольку задействованы три хука ( \everypar
, \everyvbox
, ), я определил рекурсивную процедуру , в которой вы можете предоставить список кортежей, первый компонент которой обозначает хук, второй компонент обозначает токены, которые этот хук должен доставить перед восстановлением всех хуков в списке кортежей и доставкой токенов, которые хук также доставил перед переопределением.\everyhbox
\prependtorestorehooks
Также необходимо принять некоторые меры предосторожности в случае, если механизм \Command
является вложенным.
Предостережения:
Добавление к хукам/восстановление хуков работает только до тех пор, пока сами хуки не содержат
\outer
-токенов. Например, следующий код выдает сообщение об ошибке! 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
срабатывает при создании\hbox
/\vbox
независимо от того, попадает ли этот ящик в выходной файл или попадает в регистр ящиков, содержимое которого никогда не попадет в выходной файл. Таким образом, механизм можно обмануть, поместив, например, -присваивание\setbox
в начало\Command
аргумента .Временный
\vbox
создается из\Command
первого аргумента для установки\if...
-switch в случае, если\everyhbox
/\everyvbox
срабатывает до\everypar
. Таким образом, материал\Command
первого аргумента обрабатывается дважды: один раз для создания временного блока. Один раз для создания абзаца, который должен попасть в выходной файл. Могут быть такие вещи,\immediate\write
которые вы не хотите выполнять дважды. К сожалению, нет режима "get-box-properties"/"box-measuring", в котором такие вещи можно было бы отключить.
\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
необходимо переключение в горизонтальный режим.