Выровняйте поле по левому краю, сохраняя \parindent glue/соблюдая \noindent, если он есть

Выровняйте поле по левому краю, сохраняя \parindent glue/соблюдая \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сработает раньше \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необходимо переключение в горизонтальный режим.

Связанный контент