## LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

 Options: Use Forum View Use Proportional Font Show HTML Part by Default Condense Mail Headers Message: [<< First] [< Prev] [Next >] [Last >>] Topic: [<< First] [< Prev] [Next >] [Last >>] Author: [<< First] [< Prev] [Next >] [Last >>]

Lately I've been working on a set of macros which edit the contents of
a galley vertical list; in particular they remove some and add other
things to the lines of a paragraph that has already been broken. So far I
have only used this for the paragraphs of a macrocode-like environment
which additionally tries to break codelines that are too long to fit on
one printed line, but now it occurs to me that this mechanism might be
useful in more general contexts.

Environments like macrocode are convenient in that one can have full
control over which commands get executed and thus also full control over
what gets put in the vertical list; in particular one can ensure that only
removeable items (such as boxes, penalties, and glue; kerns are oc course
also removeable, but TeX doesn't automatically insert these so I might just
as well forbid them as well) appear on the vertical list. This means that I
can use a \lastbox recursion (similar to that of the \removehboxes macro
on p. 399 of The TeXbook) to pop all boxes (lines) off the vlist and then
do something to them before I put them back. In the present case this "do
something" consists of appending a backslash to each line and possibly
changing the justification. (A backslash at the end of a line escapes
the following newline, which would otherwise have been a command
separator, in the language I want to typeset code from.) Of course this
means one has to start a new vbox to do these tricks in since material
cannot be removed from the main vertical list and one has to be careful
about the \prevdepth and a couple of other things, but those are minor
technicalities. I have included a code example in an appendix below, just
in case anyone is interested.

Outside such special environments things are less nice, since the user can
insert non-removable material anywhere between the lines of a paragraph
using for example \mark or \insert; one would have to go to great lengths
to ensure nothing of this kind will appear between the lines of the
paragraph. What makes all this relevant to LaTeX-L however is that
LaTeX2e* already does quite a lot to control what gets put on the vertical
list, so in a fully developed 2e* system it might not be that hard to
impose the condition that the contents of a galley should be
removable/editable. It is probably too much to ask for direct support in
the kernel (or is that kernel packages?), but perhaps some kind of
interface for mechanisms like the one described above could be offered?
(That's the main point in this letter, just in case anyone wonders.)

There are two more things which might need pointing out. The first is that
the prohibition of non-removable items in the vertical list that is to be
edited does not prohibit them from being there in the edited vertical list;
the trick is to hide them inside a vbox which is unboxed when the edited
material is put back on the list. Thus instead of saying \mark{<text>}
somewhere in the paragraph one might say \vadjust{\vbox{\mark{<text>}}}.
The second is that there is an application in "real" typography of editing
the contents of galleys. In Omega there are two primitives \localleftbox
and \localrightbox which insert fixed-width material to the right and left
respectively of the lines of a paragraph; this is employed for quoted
material in French typography. With editing of galley contents one could
instead insert that material after the paragraph is broken, no matter what
kind of breakpoint is chosen (if all breakpoints in the quote are
explicitly specified then it is of course much easier to use
\discretionary), but one also has to determine the shape of the paragraph
minus this inserted material (this can be done automatically too, but it
might require breaking the paragraph several times). It is certainly not
as elegant as in Omega, but it _can_ be done with a standard TeX.

Lars Hellström

PS: I'm still waiting for the reaction to my letter on xparse of 2000/08/03.

APPENDIX: Code example of macros that edit galley contents.

% \begin{macro}{\TD@reformat@lines}
%   The |\TD@reformat@lines| macro calls itself recursively to reformat
%   all lines on the current vertical list. The first line will remain
%   flush left, but all other lines will be reset flush right. The
%   visible material on the last line will be left as it is, but the
%   last box in all other lines will be replaced by a non-macro font
%   backslash.
%
%   It is very important that the current vertical list is not the main
%   vertical list.
%   It is assumed that the current vertical list consists of a sequence
%   of \meta{box}, \meta{penalty}, \meta{glue}, with an extra glue item
%   at the top of the list. It is OK if some penalty or glue item is
%   missing. In case the list contains other material as well the line
%   reformatting may be stopped prematurely, but there is a trick that
%   allows one to put arbitrary material between the lines of the
%   reformatted paragraph: rather than doing e.g.
%   \begin{quote}
%     |\mark|\marg{text}
%   \end{quote}
%   in the paragraph, do
%   \begin{quote}
%   \end{quote}
%   The |\vbox| will be recognised by the paragraph reformatting
%   mechanism as a container for vertical mode material that appears
%   between the lines of the paragraph, so it will simply be unboxed.
%   \changes{2.12}{2000/11/11}{\cs{vbox} containers for vertical
%      material are allowed between the lines of a reformatted
%      paragraph. (LH)}
%
%   Each line's horizontal list ends with
%   \begin{itemize}
%     \item a box (which contains the space that is to be replaced by a
%       backlsash),
%     \item a penalty (at which the paragraph was broken), and
%     \item a glue item (the |\rightskip|).
%   \end{itemize}
%
%   A tricky feature in the implementation is that the
%   |\bgroup|--|\egroup| nesting will be off by one. The |\bgroup| at
%   the beginning of a |\TD@reformat@line| will be matched by the
%   |\egroup| at the end of the |\TD@reformat@line| that the first one
%   calls!
%    \begin{macrocode}
\def\TD@reformat@lines{%
\bgroup
\unskip
\count@=\lastpenalty  \unpenalty
\setbox\z@=\lastbox
\ifvoid\z@
%    \end{macrocode}
%   The recursion had already decended down to the last line of the
%   paragraph, and it is now time to reformat it.
%    \begin{macrocode}
\egroup
\prevdepth=\TD@prevdepth
\hbox{%
\unhbox\z@
\unskip \unpenalty
\setbox\z@=\lastbox
\copy\TD@backslash@box
}%
\else
%    \end{macrocode}
%   Else there may be another line, and the |\TD@reformat@lines| recursion
%   must continue to descend. Upon return the box currently in box
%   register zero must be reformatted as a non-first line (flush
%   right) and it cannot be the last line in the paragraph, so it is
%   always correct to replace the last box by a backslash.
%    \begin{macrocode}
\TD@reformat@lines
\ifvbox\z@ \unvbox\z@ \else
\hb@xt@\dimen@{%
\hfill
\unhbox\z@
\unskip \unpenalty
\setbox\z@=\lastbox
\copy\TD@backslash@box
}%
\fi
\fi
\ifnum \count@=\z@ \else \penalty\count@ \fi
\egroup
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\TD@reformat@par}
%   The |\TD@reformat@par| macro reformats all lines (they're supposed
%   to constitute a paragraph, but that isn't so important) in the
%   current vertical list. The restrictions of |\TD@reformat@lines| on
%   what may appear in the list apply. |\dimen@| is used to hold the
%   desired width of reformatted paragraphs.
%
%   More precisely |\TD@reformat@par| takes care of the last line of
%   the paragraph and the possible |\vbox| containers for vertical
%   material that may follow it. Everything in the paragraph that comes
%   \changes{2.12}{2000/11/11}{\cs{vbox} containers for vertical
%      material are allowed after the last line of a reformatted
%      paragraph. (LH)}
%    \begin{macrocode}
\def\TD@reformat@par{%
\unskip
\count@=\lastpenalty  \unpenalty
\setbox\z@=\lastbox
\ifvbox\z@
\bgroup
\TD@reformat@par
\egroup
\unvbox\z@
\else\ifnum \prevgraf>\@ne
\dimen@=\@totalleftmargin
\bgroup
\unskip
\count@=\lastpenalty  \unpenalty
\setbox\z@=\lastbox
\TD@reformat@lines
\hb@xt@\dimen@{\hfill \unhbox\z@ \unskip}%
\else
\unskip
\prevdepth=\TD@prevdepth
\box\z@
\fi\fi
\ifnum \count@=\z@ \else \penalty\count@ \fi
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\TD@prevdepth}
%   |\TD@prevdepth| is a macro which is used for storing the value of
%   |\prevdepth| at times where \TeX\ modifies this special dimen in
%   unwanted ways. It should always be set globally.
% \end{macro}
%
%
% \begin{macro}{\TD@begin@tclpar}
%   The |\TD@begin@tclpar| macro is called when a paragraph in a
%   \texttt{tcl} or \texttt{tcl*} environment is about to start. It
%   takes care of setting up things so that the paragraph can later be
%   reformatted using |\TD@reformat@par|, but it also has to make sure
%   that this reformatting doesn't affect the way the paragraph blends
%   in with vertical material before and after it.
%
%   Reformatting requires that the paragraph is first built in
%   restricted vertical mode, i.e., it has to be built in an explicit
%   |\vbox|. A problem with this is however that it changes the value of
%   |\prevdepth|, which must therefore be explicitly restored.
%    \begin{macrocode}
\def\TD@begin@tclpar{%
\xdef\TD@prevdepth{\the\prevdepth}%
\setbox\z@=\vbox\bgroup
\color@begingroup
\prevdepth=\TD@prevdepth
\indent
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\TD@end@tclpar}
%   The |\TD@end@tclpar| macro ends a paragraph begun by
%   |\TD@begin@tclpar|, reformats it (|\TD@reformat@par|), and
%   contributes it to the surrounding vertical list. The |\begingroup|
%   and |\endgroup| are there to sort things out in case the recursion
%   in |\TD@reformat@par| fails to match as intended. The second
%   |\@@par| sees to that the page builder is exercised (without it,
%   several pages may go onto the main vertical list without anything
%   being shipped out).
%    \begin{macrocode}
\def\TD@end@tclpar{%
\@@par
\begingroup
\skip@=\lastskip
\TD@reformat@par
\vskip\skip@
\endgroup
\xdef\TD@prevdepth{\the\prevdepth}%
\color@endgroup
\egroup
\unvbox\z@
\prevdepth=\TD@prevdepth
\@@par
}
%    \end{macrocode}
% \end{macro}