1

How to define a command which takes two arguments and from the first argument makes a new paragraph where the right margin of a box containing the second argument is justified at the left margin of that paragraph with the reference-point of that box vertically being on the baseline of the first line of text of that paragraph?

If I do

\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

, then (as expected) \noindent has no effect and the right margin of the box containing the phrase "Left justified?" is not justified at the left margin of the paragraph but is justified at the left of the letter T.

If instead of \leavevmode I do \noindent, then the right margin of the box containing the phrase "Left justified?" is justified both at the left margin of the paragraph and at the left of the letter T but there will not be \parindent-glue before the letter T unless you add it "by hand" as a part of \Command's first argument:

\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

So after spending days of parsing inscrutable ambiguous phrases of the TeXbook, I did only find ways of not achieving what I desire and I only learned why my attempts do not work.

Could somebody point out a way which does work?

2
  • TeXBook, Chapter 14: How TeX Breaks Paragraphs into Lines, double dangerous bend paragraph right behind exercise 14.28 says: When TeX enters horizontal mode, the following things always happen: Vertical \parskip-glue. Then initiating horizontal list by hbox of width \parindent (unless horizontal mode was initiated by \noindent), then the tokens held in \everypar, then processing the... Aug 31, 2020 at 17:42
  • ... tokens that caused horizontal mode. Via \everypar you can "lay hands" on the \parindent-box via \lastbox which will remove the last box from the list and move it to the box-register of your choice. This is what David Carlisle did in his answer. Aug 31, 2020 at 17:43

2 Answers 2

0

enter image description here

I think you mean this, not entirely clear:

\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
7
  • Almost. Thank you. \Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?} does not work out... Aug 31, 2020 at 9:44
  • @MarsupialWallaby starting text with \hbox is an explicit instruction not to start a paragraph. Aug 31, 2020 at 9:50
  • Yes. But I would like to override that with \Command. That's why I said " and from the first argument makes a new paragraph". The problem is ensuring that a paragraph will be started without the means applied for ensuring causing or preventing the insertion of \parindent-glue. Aug 31, 2020 at 9:55
  • @MarsupialWallaby without your comand if your text is \hbox{a}\hbox{b}\hbox{c}ddd then a b c stack vertically and the paragraph and any indentation starts at ddd. Your llap is essentially a paragraph label as far as I can see so comes with the parindent on ddd. You could possibly do something else but I don't see what in general you can do if you are allowing non paragraph commands like hbox or vskip at the start of that argument. Aug 31, 2020 at 9:59
  • note parindent is a box, not glue. Aug 31, 2020 at 9:59
0

I took your comment

Almost. Thank you. \Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?} does not work out...

on David Carlisle's answer into account:

The horizontal box of width \parindent at the beginning of horizontal mode is produced and set without carrying out the hook \everyhbox.

Thus you can create a mechanism for setting an \if...-switch in case \everyhbox/\everyvbox is triggered before \everypar:

Create a temporary vertical box from \Command's first argument.

While creating that temporary vertical box prepend to the hooks \everyhbox/\everyvbox a macro-token which globally sets an \if...-switch.
Prepend to each of the hooks \everyhbox/\everyvbox and \everypar a directive to restore all these hooks.

Effect:

In case \everypar is carried out before \everyvbox or \everyhbox, the hooks will be restored and thus the directive to change the \if..-switch appended to \everyhbox/\everyvbox is gone and will never be carried out.

In case \everyvbox or \everyhbox is carried out before \everypar, the directive to set the \if-switch is carried out once and all the hooks are restored.

As three hooks (\everypar, \everyvbox, \everyhbox) are involved, I defined a recursive routine \prependtorestorehooks where you can provide a list of tuples, first component denoting a hook, second component denoting tokens for this hook to deliver before restoring all hooks in the tuple-list and delivering the tokens which the hook also delivered before redefining.

You also need some precautions in case the mechanism/\Command is nested.

Caveats:

  • Appending to hooks/restoring hooks only works as long as the hooks themselves do not contain \outer-tokens. E.g., the following code yields an error-message ! 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 is triggered when creating a \hbox/\vbox no matter if that box makes it into the output-file or goes into a box-register whose content will never make it into the output-file. Thus the mechanism can be fooled by placing, e.g., a \setbox-assignment in the beginning of \Command's argument.

  • A temporary \vbox is created from \Command's first argument for setting the \if...-switch in case \everyhbox/\everyvbox is triggered before \everypar. Thus the material of \Command's first argument is processed twice: Once for creating the temporary box. Once for creating the paragraph that should go into the output-file. There might be things like \immediate\write which you don't want to have carried out twice. Unfortunately there is no "get-box-properties"/"box-measuring"-mode where such things could be turned off.

 

\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

enter image description here

To be honest:

I don't know why all this is needed. In the manual of \Command just tell that TeX switches to vertical mode before processing #1. The user can decide her-/himself if explicit \leavevmode or \noindent is needed for switching to horizontal-mode.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.