
Como definir um comando que recebe dois argumentos e a partir do primeiro argumento cria um novo parágrafo onde a margem direita de uma caixa contendo o segundo argumento é justificada na margem esquerda desse parágrafo com o ponto de referência dessa caixa verticalmente no linha de base da primeira linha do texto desse parágrafo?
Se eu fizer
\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
, então (como esperado) \noindent
não tem efeito e a margem direita da caixa contém a frase "Justificado à esquerda?" não é justificado na margem esquerda do parágrafo, mas é justificado à esquerda da letra T
.
Se em vez de \leavevmode
I do \noindent
, então a margem direita da caixa contendo a frase "Justificado à esquerda?" é justificado tanto na margem esquerda do parágrafo quanto à esquerda da letra, T
mas não haverá \parindent
-glue antes da letra, T
a menos que você adicione "à mão" como parte do \Command
primeiro argumento de ':
\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
Então, depois de passar dias analisando frases ambíguas e inescrutáveis do TeXbook, só encontrei maneiras de não alcançar o que desejo e só aprendi por que minhas tentativas não funcionam.
Alguém poderia apontar uma maneira quefaztrabalhar?
Responder1
Responder2
peguei seu comentário
Quase. Obrigado.
\Command{\hbox{This box should be part of the paragraph, too} text text}{Left justified?}
não dá certo...
sobreA resposta de David Carlisleem conta:
A caixa horizontal de largura \parindent
no início do modo horizontal é produzida e colocada sem realizar o gancho \everyhbox
.
Assim, você pode criar um mecanismo para definir um \if...
-switch caso \everyhbox
/ \everyvbox
seja acionado antes \everypar
:
Crie uma caixa vertical temporária a partir \Command
do primeiro argumento de.
Ao criar aquela caixa vertical temporária, acrescente aos ganchos \everyhbox
/ \everyvbox
um token de macro que define globalmente um \if...
-switch.
Anexe a cada um dos ganchos \everyhbox
/ \everyvbox
e \everypar
uma diretiva para restaurar todos esses ganchos.
Efeito:
Caso \everypar
seja executado antes de \everyvbox
ou \everyhbox
, os ganchos serão restaurados e, portanto, a diretiva para alterar o \if..
-switch anexado a \everyhbox
/ \everyvbox
desaparece e nunca será executada.
No caso \everyvbox
de ou \everyhbox
ser executado antes \everypar
, a diretiva para definir o \if
-switch é executada uma vez e todos os ganchos são restaurados.
Como três ganchos ( \everypar
, \everyvbox
, \everyhbox
) estão envolvidos, defini uma rotina recursiva \prependtorestorehooks
onde você pode fornecer uma lista de tuplas, o primeiro componente denotando um gancho, o segundo componente denotando tokens para este gancho entregar antes de restaurar todos os ganchos na lista de tuplas e entregar os tokens que o gancho também entregou antes da redefinição.
Você também precisa de alguns cuidados caso o mecanismo \Command
esteja aninhado.
Ressalvas:
Anexar a ganchos/restaurar ganchos só funciona enquanto os próprios ganchos não contiverem
\outer
-tokens. Por exemplo, o código a seguir gera uma mensagem de erro! 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
é acionado ao criar um\hbox
/\vbox
não importa se essa caixa entra no arquivo de saída ou vai para um registro de caixa cujo conteúdo nunca entrará no arquivo de saída. Assim, o mecanismo pode ser enganado colocando, por exemplo, um\setbox
-assignment no início do\Command
argumento de '.Um temporário
\vbox
é criado a partir\Command
do primeiro argumento de para definir o\if...
-switch caso\everyhbox
/\everyvbox
seja acionado antes\everypar
. Assim, o material do\Command
primeiro argumento de é processado duas vezes: Uma vez para criar a caixa temporária. Uma vez para criar o parágrafo que deve ir para o arquivo de saída. Pode haver coisas\immediate\write
que você não gostaria de fazer duas vezes. Infelizmente não existe um modo "get-box-properties"/"box-measuring" onde tais coisas possam ser desligadas.
\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
Para ser honesto:
Não sei por que tudo isso é necessário. No manual \Command
apenas informamos que o TeX muda para o modo vertical antes do processamento #1
. O usuário pode decidir por si mesmo se é explícito \leavevmode
ou \noindent
necessário para mudar para o modo horizontal.