Received: via tmail-4.1(11) (invoked by user schoepf) for schoepf; Mon, 13 Nov 2000 18:22:18 +0100 (MET) Received: from mailgate1.zdv.Uni-Mainz.DE (mailgate1.zdv.Uni-Mainz.DE [134.93.8.56]) by mail.Uni-Mainz.DE (8.9.3/8.9.3) with ESMTP id SAA29090 for ; Mon, 13 Nov 2000 18:22:18 +0100 (MET) MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----_=_NextPart_001_01C04D96.45ED8100" Received: from mail.listserv.gmd.de (mail.listserv.gmd.de [192.88.97.5]) by mailgate1.zdv.Uni-Mainz.DE (8.11.0/8.10.2) with ESMTP id eADHMHR21517 for ; Mon, 13 Nov 2000 18:22:17 +0100 (MET) Received: from mail.listserv.gmd.de (192.88.97.5) by mail.listserv.gmd.de (LSMTP for OpenVMS v1.1a) with SMTP id <12.1D92F8ED@mail.listserv.gmd.de>; Mon, 13 Nov 2000 18:22:16 +0100 X-MimeOLE: Produced By Microsoft Exchange V6.5 Received: from RELAY.URZ.UNI-HEIDELBERG.DE by RELAY.URZ.UNI-HEIDELBERG.DE (LISTSERV-TCP/IP release 1.8b) with spool id 478666 for LATEX-L@RELAY.URZ.UNI-HEIDELBERG.DE; Mon, 13 Nov 2000 18:22:14 +0100 Received: from ix.urz.uni-heidelberg.de (mail.urz.uni-heidelberg.de [129.206.119.234]) by relay.urz.uni-heidelberg.de (8.8.8/8.8.8) with ESMTP id SAA17370 for ; Mon, 13 Nov 2000 18:22:13 +0100 (MET) Received: from relay.uni-heidelberg.de (relay.uni-heidelberg.de [129.206.100.212]) by ix.urz.uni-heidelberg.de (8.8.8/8.8.8) with ESMTP id SAA23966 for ; Mon, 13 Nov 2000 18:22:12 +0100 Received: from abel.math.umu.se (abel.math.umu.se [130.239.20.139]) by relay.uni-heidelberg.de (8.9.3+Sun/8.9.3) with ESMTP id SAA26811 for ; Mon, 13 Nov 2000 18:22:12 +0100 (MET) Received: from [130.239.20.144] (mac144.math.umu.se [130.239.20.144]) by abel.math.umu.se (8.9.2/8.9.2) with ESMTP id SAA12913 for ; Mon, 13 Nov 2000 18:21:29 +0100 (CET) Return-Path: X-Sender: lars@abel.math.umu.se x-mime-autoconverted: from quoted-printable to 8bit by relay.urz.uni-heidelberg.de id SAA17371 Content-class: urn:content-classes:message Subject: Editing galley contents Date: Mon, 13 Nov 2000 18:22:02 +0100 Message-ID: X-MS-Has-Attach: X-MS-TNEF-Correlator: From: =?iso-8859-1?Q?Lars_Hellstr=F6m?= Sender: "Mailing list for the LaTeX3 project" To: "Multiple recipients of list LATEX-L" Reply-To: "Mailing list for the LaTeX3 project" Status: R X-Status: X-Keywords: X-UID: 3631 This is a multi-part message in MIME format. ------_=_NextPart_001_01C04D96.45ED8100 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable 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{} somewhere in the paragraph one might say \vadjust{\vbox{\mark{}}}. 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=F6m 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} % |\vadjust{\vbox{\mark{|\meta{text}|}}}| % \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@=3D\lastpenalty \unpenalty \setbox\z@=3D\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=3D\TD@prevdepth \hbox{% \unhbox\z@ \unskip \unpenalty \setbox\z@=3D\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@=3D\lastbox \copy\TD@backslash@box }% \fi \fi \ifnum \count@=3D\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 % before the last line is handled by |\TD@reformat@lines|. % \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@=3D\lastpenalty \unpenalty \setbox\z@=3D\lastbox \ifvbox\z@ \bgroup \TD@reformat@par \egroup \unvbox\z@ \else\ifnum \prevgraf>\@ne \dimen@=3D\@totalleftmargin \advance \dimen@ \linewidth \bgroup \unskip \count@=3D\lastpenalty \unpenalty \setbox\z@=3D\lastbox \TD@reformat@lines \hb@xt@\dimen@{\hfill \unhbox\z@ \unskip}% \else \unskip \prevdepth=3D\TD@prevdepth \box\z@ \fi\fi \ifnum \count@=3D\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@=3D\vbox\bgroup \color@begingroup \prevdepth=3D\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@=3D\lastskip \TD@reformat@par \vskip\skip@ \endgroup \xdef\TD@prevdepth{\the\prevdepth}% \color@endgroup \egroup \unvbox\z@ \prevdepth=3D\TD@prevdepth \@@par } % \end{macrocode} % \end{macro} ------_=_NextPart_001_01C04D96.45ED8100 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Editing galley contents

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=F6m

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}
%     = |\vadjust{\vbox{\mark{|\meta{text}|}}}|
%   \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@=3D\lastpenalty  = \unpenalty
   \setbox\z@=3D\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=3D\TD@prevdepth
      \hbox{%
         = \unhbox\z@
         = \unskip \unpenalty
         = \setbox\z@=3D\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@{%
          &nbs= p; \hfill
          &nbs= p; \unhbox\z@
          &nbs= p; \unskip \unpenalty
          &nbs= p; \setbox\z@=3D\lastbox
          &nbs= p; \copy\TD@backslash@box
         = }%
      \fi
   \fi
   \ifnum \count@=3D\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
%   before the last line is handled by = |\TD@reformat@lines|.
%   \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@=3D\lastpenalty  = \unpenalty
   \setbox\z@=3D\lastbox
   \ifvbox\z@
      \bgroup
      = \TD@reformat@par
      \egroup
      \unvbox\z@
   \else\ifnum \prevgraf>\@ne
      = \dimen@=3D\@totalleftmargin
      \advance \dimen@ = \linewidth
      \bgroup
      \unskip
      = \count@=3D\lastpenalty  \unpenalty
      = \setbox\z@=3D\lastbox
      = \TD@reformat@lines
      \hb@xt@\dimen@{\hfill = \unhbox\z@ \unskip}%
   \else
      \unskip
      = \prevdepth=3D\TD@prevdepth
      \box\z@
   \fi\fi
   \ifnum \count@=3D\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@=3D\vbox\bgroup
      = \color@begingroup
      = \prevdepth=3D\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@=3D\lastskip
         = \TD@reformat@par
         = \vskip\skip@
      \endgroup
      = \xdef\TD@prevdepth{\the\prevdepth}%
      \color@endgroup
   \egroup
   \unvbox\z@
   \prevdepth=3D\TD@prevdepth
   \@@par
}
%    \end{macrocode}
% \end{macro}

------_=_NextPart_001_01C04D96.45ED8100--