
Wie definiert man einen Befehl, der zwei Argumente annimmt und aus dem ersten Argument einen neuen Absatz erstellt, bei dem der rechte Rand eines Felds, das das zweite Argument enthält, am linken Rand dieses Absatzes ausgerichtet ist, wobei sich der Bezugspunkt dieses Felds vertikal auf der Grundlinie der ersten Textzeile dieses Absatzes befindet?
Wenn ich mache
\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
, hat dann (wie erwartet) \noindent
keine Wirkung und der rechte Rand des Kästchens mit der Phrase „Linksbündig?“ wird nicht am linken Rand des Absatzes, sondern links vom Buchstaben ausgerichtet T
.
Wenn anstelle von \leavevmode
I do \noindent
der rechte Rand des Felds mit der Phrase „Linksbündig?“ gesetzt wird, wird dieser sowohl am linken Rand des Absatzes als auch links vom Buchstaben ausgerichtet. Vor dem Buchstaben T
wird jedoch kein -glue angezeigt, sofern Sie es nicht „von Hand“ als Teil des ersten Arguments hinzufügen:\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
Nachdem ich also Tage damit verbracht hatte, die unverständlichen, mehrdeutigen Sätze des TeXbook zu analysieren, fand ich nur Wege, die mich daran hinderten, mein gewünschtes Ziel zu erreichen, und ich erfuhr nur, warum meine Versuche nicht funktionierten.
Könnte jemand einen Weg aufzeigen, dertutarbeiten?
Antwort1
Antwort2
Ich habe deinen Kommentar
Fast. Danke.
\Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?}
klappt nicht...
AnDavid Carlisles Antwortberücksichtigen:
Die horizontale Box der Breite \parindent
am Anfang des Horizontalmodus wird ohne Ausführen des Hakens erzeugt und gesetzt \everyhbox
.
Auf diese Weise können Sie einen Mechanismus zum Setzen eines \if...
-Schalters erstellen, falls \everyhbox
/ \everyvbox
vor ausgelöst wird \everypar
:
\Command
Erstellen Sie aus dem ersten Argument eine temporäre vertikale Box .
Beim Erstellen dieser temporären vertikalen Box stellen Sie den Hooks \everyhbox
/ \everyvbox
ein Makro-Token voran, das global einen \if...
-Switch setzt.
Stellen Sie jedem der Hooks \everyhbox
/ \everyvbox
und \everypar
eine Anweisung voran, um alle diese Hooks wiederherzustellen.
Wirkung:
Falls vor oder \everypar
ausgeführt wird , werden die Hooks wiederhergestellt und damit ist die Anweisung zum Ändern des an / angehängten -Schalters weg und wird nie ausgeführt.\everyvbox
\everyhbox
\if..
\everyhbox
\everyvbox
Falls \everyvbox
oder \everyhbox
vorher ausgeführt wird \everypar
, wird die Anweisung zum Setzen des \if
-Schalters einmal ausgeführt und alle Hooks werden wiederhergestellt.
Da drei Hooks ( \everypar
, \everyvbox
, \everyhbox
) beteiligt sind, habe ich eine rekursive Routine definiert, \prependtorestorehooks
in der Sie eine Liste von Tupeln bereitstellen können, wobei die erste Komponente einen Hook bezeichnet und die zweite Komponente Token bezeichnet, die dieser Hook liefern soll, bevor alle Hooks in der Tupelliste wiederhergestellt und die Token geliefert werden, die der Hook auch vor der Neudefinition geliefert hat.
Sie müssen auch einige Vorsichtsmaßnahmen treffen, falls der Mechanismus/ \Command
verschachtelt ist.
Vorbehalte:
Das Anhängen an Hooks bzw. Wiederherstellen von Hooks funktioniert nur, solange die Hooks selbst keine
\outer
-Tokens enthalten. Der folgende Code erzeugt beispielsweise eine Fehlermeldung! 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
wird beim Erstellen eines\hbox
/ ausgelöst\vbox
, unabhängig davon, ob diese Box in die Ausgabedatei gelangt oder in ein Box-Register, dessen Inhalt niemals in die Ausgabedatei gelangt. Der Mechanismus kann also ausgetrickst werden, indem beispielsweise eine -Zuweisung\setbox
an den Anfang des\Command
Arguments von gesetzt wird.\vbox
Aus dem ersten Argument von wird ein temporäres Element\Command
zum Setzen des\if...
-Schalters erstellt, falls\everyhbox
/\everyvbox
vor ausgelöst wird\everypar
. Somit\Command
wird das Material des ersten Arguments von zweimal verarbeitet: Einmal zum Erstellen der temporären Box. Einmal zum Erstellen des Absatzes, der in die Ausgabedatei aufgenommen werden soll. Es gibt möglicherweise Dinge,\immediate\write
die Sie nicht zweimal ausführen möchten. Leider gibt es keinen "get-box-properties"/"box-measuring"-Modus, in dem solche Dinge abgeschaltet werden könnten.
\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
Ehrlich gesagt:
Ich weiß nicht, warum das alles nötig ist. Im Handbuch \Command
steht nur, dass TeX vor der Verarbeitung in den vertikalen Modus wechselt #1
. Der Benutzer kann selbst entscheiden, ob er explizit in den horizontalen Modus wechseln muss \leavevmode
oder \noindent
nicht.