% This is CHBARS.STY as of Aug '92 % -*-LaTeX-*- %--------------------------------------------------------- % (c) 1989 by J.Schrod. copy conditions see below. % % Macro package for creating changebars in LaTeX. % MAKEPROG will ``weave'' this file into documentation that can be LaTeX'ed. % % documented in LaTeX (for Anne and Chris) % % VERSION HISTORY (MSCF -- most significant change first) % % DATE PERSON REMARK % 92-08-15 -rlb Use change bars in this document to mark major changes; % so now you can see for yourself what they are. % 92-07-28 -rlb Run through a spelling checker before distributing. % 92-01-15 -rlb 1) Keep \maxdeadcycles the same; just % don't count calls to output for change processing. % 2) Allow setting the change bar width. % 3) Some typos and rewording of some of the comments. % 4) Allow setting change bar width. Interface copied % from changebars.sty % 5) Allow changebars to be on the left as well as % the right. % 6) localize some variables global definitions now % start with cb_. % 7) Chain onto existing \output routine rather than assume % \plainoutput. % And miscellaneous minor hacks. % 89-10-09 -js converted to LaTeX (progltx) % 89-09-25 -js repaired \mark processing in horizontal mode % 89-08 -js first version (for EuroTeX89 in Karlsruhe) % % author's current address: % % Detig$\,\cdot\,$Schrod \TeX{}sys % Joachim Schrod % Kranichweg 1 % % D-6074 R\"odermark-Urberach % FR Germany % % Tel. (+6074) 1617 % Bitnet: XITIJSCH@DDATHD21 % should be progtex... % % % These TeX macros were documented with the documentation system % MAKEPROG and automatically converted to the current form. % If you have MAKEPROG available you may transform it back to % the original input: Remove every occurence of three percents % and one optional blank from the beginning of a line and remove % every line which starts with four percents. The following lex % program will do this: % % %% % % ^%%%\ ? ; % ^%%%%.*\n ; % % MAKEPROG may be obtained over the net from the Bitnet-Listserver % LISTSERV@DHDURZ1 (filelist WEBWARE), from tuglib@science.utah.edu, % or via ftp from june.cs.washington.edu. % % \documentstyle[progltx,chbars,a4-9]{article} % % local macros % \let\mc=\small % for names like GNU \def\PS{{\sc PostScript}} \def\DVI{{\tt DVI}} \def\GNU{{\mc GNU}} \chardef\bs=`\\ % \begin{document} \tableofcontents \title{Changebars without {\tt \bs{}special}'s} \author{\sc Joachim Schrod} \newcommand{\changedate}{Aug 15, 1992} \date{Revised last on \changedate\footnote{by {\tt rocky@watson.ibm.com}}\\ Formatted on \today} \maketitle \begin{abstract} It is common practice to use vertical bars in the margins of a document to mark pieces of text which have changed since the last version(s) of this document. Such vertical bars are usually called {\em changebars}. It has often been said that it is impossible to produce changebars with \TeX{} without the usage of |\special| commands (driver directives), which extend the primitives of \TeX{}. This paper presents a \TeX{} macro file which implements changebars without such a usage. The macro file is written for the usage with {\sc Plain}~\TeX{} but the implementation strategy can be used with \LaTeX{}, too with minor changes. \end{abstract} \chap Introduction. Changebars are used to mark modified parts in existing documents. For the usage in \TeX{} documents, there exist only solutions that use driver/printer features by the way of inserting |\special| commands in the \TeX{} source, e.g.\ for \PS{} drivers. This results in documents that are no longer as freely interchangeable as the \DVI{} concept would allow---device dependency is problematic especially for this application that is useful for multi-authoring or standards development. This macro package offers a pure \TeX{} solution. Nevertheless, it has its restrictions, too. The page break will no longer be optimal, because there is no strechability or shrinkability of a page on top of the last region of change marked. But this seems to be acceptable, especially as the change bar feature often will be used for proof reading and not in the final document. This restriction is the reason why no change marks can be used on title pages or on similar constructions. Changes in floating insertions (footnotes, figures) are not handled. Multi-column formats such as a two-column layout will not work. There is currently no support for nested changes. The method for writing a change bar consists of three parts: First, the output routine is signalled when the beginning or end of a change bar setting command is encountered. Next, the position of the changed area is found out and fixed; the end-of-change command adds the last change bar position, length and width to a list of all such positions, lengths and widths of change bars that is accumulated for the current page. Finally, when the output routine is triggered (either asynchronously in trying to ship out the page or synchronously when discovering that the end of a change bar lies off the page), this list of change bars information is used create vertical rules which are added to the page. If the change bar is not complete, a ``virtual'' end change is inserted, and a ``virtual'' begin change is inserted at the beginning of the next page. \beginchange The demonstrated solution was originally written in {\sc Plain} \TeX{}, because it was easier and could be presented better at the Euro\TeX89 conference in Karlsruhe. An adaptation to \LaTeX{} has been done too which requires minor modifications to the \LaTeX's output routine. \endchange Some history. The routines were initially written by Joachim Schrod. Around Jan.\ 1992, R.\ Bernstein added some of the features coded in |changebars.sty| to combine the best features of the two (and added a couple of his own). For example, the ability to specify change bar widths, put the change bars either on the right or left margin, specifiy the distance from the margin to the change bar, and chain to on top of a pre-existing modified output routine. \beginchange Initially, both the {\TeX} and the {\LaTeX} versions were put into one file. However, due problems in dealing with conditional definition of code, in particular problems with an extra or omitted |\fi| in defining or not |\ifr@ggedbottom|, the code was split into two. \endchange The |changebars.sty| package was written by Michael Fine and revised by Johannes Braams and Neil Winton. One or two ideas from Thomas J.~Reid have been used. \chap GNU General Public License. This program is free software; you can redistribute it and/or modify it under the terms of the \GNU{} General Public License as published by the Free Software Foundation; either version~2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but {\bf without any warranty\/}; without even the implied warranty of {\bf merchantability\/} or {\bf fitness for a particular purpose}. See the \GNU{} General Public License for more details. You should have received a copy of the \GNU{} General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675~Mass Ave, Cambridge, MA~02139, USA. \chap User Interface. \begin{newsloppy} A changed area is described by two marks, |\beginchange| and |\endchange|. The |\beginchange| can take an optional argument, the bar thickness, enclosed in brackets. That is, either of the something like |\beginchange[0.4pt]|, or |\beginchange| is valid. In the latter case where no thickness dimension is specified, the value of |\changebarwidth| is used. Therefore, by setting this you can change the bar thickness used when none is given. Changing the bar thickness might be useful in identifying different classes of changes. Another pair of global values that can be set are the logical variables {\tt \bs chbar\-Right\-true} and {\tt \bs chbar\-Right\-false}. These specify whether changebars should be on the right or left. Say, if one wanted to have the changebars always appear on the outside margin, one could change these accordingly on each page. \end{newsloppy} Finally |\BarDistance| is the amount of place between the text margin and the change bars. This value should normally be positive. For change bars on the right this value is added to |\hsize|, while for change bars on the left this value is {\em subtracted\/} from |\hoffset|. \beginprog \newdimen\changebarwidth \changebarwidth=1pt\relax \newif\ifchbarRight \chbarRightfalse \newdimen\BarDistance \BarDistance=2cc \endprog \chap Utility Routines and Programming Conventions. Before we get into the nitty gritty details, we give some common macros. First, we declare some shorthands for category codes. By declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as letters we can use them in our macros. (I agree with D.~Knuth that |\identifier_several_words_long| is more readable than |\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.) \beginchange By defining the at sign to be in the letter class, we can access {\sc Plain} \TeX's ``private''macros. By defining the underscore to be in the letter class, we make our own private macros more readable. But since have to restore these category codes at the end of this macro file, we store \endchange their former values in the control sequences |\atcode| and |\uscode|. This method is better than to use a group because not all macros have to be defined global this way. All {\em global\/} definitions of the package are prefixed by |cb_|. In this way, they can be easily determined. If other packages do likewise, it is less likely that definition names will clash between packages. (Unless the other package uses the |cb_| prefix too, in which case there is a good chance that many variable names will clash.) The {\TeX}book recommends the 255 registers be used for scratch space, so routines use this value when a spare register is needed. (See for example |\cb_write_bar|.) \beginprog \chardef\letter=11 \chardef\atcode=\catcode`\@ \chardef\uscode=\catcode`\_ \catcode`\@=\letter \catcode`\_=\letter \endprog \sect Now we are ready to code the top-level routine for indicating the beginning of a change. But first we display the banner and version number associated with this package. If this package has been loaded already we terminate. Although one can specify a bar thickness on the |\beginchange| macro, it is at the |\endchange| where this thickness is recorded and put on a current list of change bar entries for the current page. It seems more natural to specify the thickness at the beginning of the change rather than the end. Therefore at the |\beginchange| we merely save the value given or save the value of |\changebarwidth| if no thickness was given. |\cb_changebarwidth| is this saved value. Auxiliary routine |\cb_xstart| is used to parse off the optional enclosing brackets. (This weird name was taken from one used in |changebars.sty|.) default value, that is the value when a width is not specified is |\changebarwidth|. \beginprog \@ifundefined{cb_xstart}{}{\endinput} \typeout{Style option `change bars' -- version 1.1a} \newdimen\cb_changebarwidth \def\beginchange{\@ifnextchar [ {\cb_xstart} {\beginchange_\changebarwidth}} \def\cb_xstart[#1]{\beginchange_{#1}} \endprog \chap Triggering the Output Routine. \beginchange When a change bar is started or completed, we signal the output routine in the ``usual'' way described below. Why bother the output routine? The output routine is supposed to be thought of as an asynchronous process which has access to the page via the contents of box register 255. In theory, if we did not perform actions in the output routine, it might get triggered inadvertantly, and the output routine might modify this box register on us in the midst of our work. The information that needs to be recorded is the position of the beginning and end of the lines containing a marked change region. \begin{newsloppy} The macros |\beginchange| and |\endchange| signal the output routine. Rather than merely calling the output routine directly, these routines signal the output routine by setting a low page penalty, i.e., one which tells {\TeX} that this is a really good place to break the page. Of course, we will modify the output routine so that it doesn't really split the page when it has been called in this fashion. Again, the reason we signal the output routine in what seems at first a pretty odd way, is that this is the way it is supposed to be done. If a low penalty were not set, some other action (if we weren't careful) might cause a penalty to be set and thus call the output routine (recursively). So that we can distinguish our calls from others, |\beginchange| and |\endchange| reserve a range of penalty values; he actual value is stored in |\cb_break_penalty|. This range of values is below $-10\,000$, the nominal value for indicating that a page should occur now. The penalty will be in the range $|\cb_penalty_group| \cdot 100 - 99 \ldots |\cb_penalty_group| \cdot 100$. The output routine then decides whether it has been called to start or end a change bar or neither. This is done with the values |\cb_penalty_begin| and |\cb_penalty_end| that are used as the second-to-last digit of the change penalty. \end{newsloppy} The output routine also needs to determine whether the beginning of change bar starts in horizontal or vertical mode so it can figure out the exact placement for the beginning of the bar line. In horizontal mode, the change bar does not begin at the baseline of the actual text position, but on top of the actual line. This is marked in the last digit, an odd digit will be used in horizontal mode. Note that the values mentioned above are used as digits here that can be concatenated. If they are not followed by an other digit they should be terminated by |\space| to stop {\TeX}'s look-ahead scanning for digits when reading a number. \endchange \beginprog \def\cb_penalty_group{-101} \def\cb_penalty_begin{0} \def\cb_penalty_end{1} \endprog \sect The calls to start or end a change bar, set an encoded page-break penalty which includes an indication of begin or end of a change bar. The variable |\cb_break_penalty| is used to create such as special value. The rest (|\cb_trigger_output|) is the same action for both. The region just before the end of the changed region can be in horizontal mode and preceded by glue that could cause a line break, thus including the following line to the change area as well. To avoid this unwanted behavior, the space is saved in |\save_lastskip|, discarded in front of the mark and restored afterwards. \beginprog \newcount\cb_break_penalty \def\beginchange_#1{% \cb_changebarwidth=#1 \cb_break_penalty=\cb_penalty_group\cb_penalty_begin0 \cb_trigger_output } \def\endchange{{% \skipdef\save_lastskip=255% \ifhmode \save_lastskip=\lastskip \unskip \fi% \cb_break_penalty=\cb_penalty_group\cb_penalty_end0% \cb_trigger_output% \ifhmode \hskip\save_lastskip \fi% }}%\endchange \endprog \sect The next routine, |\cb_trigger_output|, triggers calls to the output routine by setting the page-break penalty, |\penalty|. {\TeX} may discard an |\output| invocation at the beginning of a page. So we trigger the output routine twice, first with a special penalty value that is 2~less than the correct value (including the code for horizontal or vertical mode). After the first page break, it is asserted that the current list is empty. The output routine has to save the former page contents if necessary. Next we set the penalty to the correct value. The second page break does the real work, restores the page contents and handles the split insertions (footnotes, figures,~\dots). In horizontal mode |\spacefactor| must not be destroyed, so it is saved and restored via local count register |\save_spacefactor|. \beginprog \def\cb_trigger_output{% \ifinner \errmessage{Change cannot be marked inside a box}% \else{% \countdef\save_spacefactor=255% \ifvmode \let\do_in_vmode=\relax \advance \cb_break_penalty by -2 \else \save_spacefactor=\spacefactor \let\do_in_vmode=\vadjust \advance \cb_break_penalty by -3 \fi \do_in_vmode{% \penalty\cb_break_penalty% first call to \output \null \advance \cb_break_penalty by 2 \penalty\cb_break_penalty% second call to \output }% \ifhmode \spacefactor=\save_spacefactor \fi }% \fi }% cb_trigger_output \endprog \sect Using the output routine for passing information has it's difficulties. One of the hard parts is \beginchange handling page marks. These are token lists which are set by the |\mark| command. They record information that can later be accessed by the output routine. The canonical example of such a use of the |\mark| command is in creating \endchange dictionary-style entry headings. The output routine can access one of three token lists through three control sequences: |\botmark| is the last page mark given, |\topmark| is the |\botmark| of the previous page, and |\firstmark| is the first page mark on the actual page or |\topmark| if none was given. Here ``page'' is used in the \TeX{} sense, i.e.\ as the material which has been collected between two |\output| invocations. Of course, the page marks must not be destroyed---and that means they must be reinserted after each special use of the output routine. But we are lucky: A ``special use'' consists of two |\output| invocations, so we can insert |\topmark| again as a page mark after the first invocation where it will be the only page mark on that \TeX{} page. The second invocation will automatically transform this page mark into the ``last page mark on the previous page,'' i.e.\ in |\topmark|---that's what we need! Furthermore |\firstmark| and |\botmark| are saved in control sequences during the first invocation, they will be inserted again, too. There's one situation where this approach doesn't work: in front of the first page mark. Here, |\topmark|, |\firstmark|, and |\botmark| expand to an empty token list. If we save them then and insert their old values we have inserted empty page marks. If other page marks follow on the same ``real'' page, |\firstmark| will be empty instead of expanding to the token list of the first page mark. To prevent this we must not save and restore page marks before the first |\mark| has been added to the main vertical list. Well, that can be controlled with a switch---but this switch must be set very carefully. If it is set immediately by the first |\mark| this may be in horizontal mode and special output invocations can occur above this page mark (i.e., there may be a |\beginchange| in the same paragraph in front of the |\mark|). Therefore the setting of the switch must be delayed until the vertical position of the |\mark| (precisely: the position of the |\mark| in the current list) is reached. In horizontal mode this can be done with a |\vadjust| and the output routine! Voil\`a, this is another command group for the output routine with only one command. \beginprog \newif\if_cb_save_mark@ \_cb_save_mark@false \def\mark_penalty_group{-102} \endprog \sect We will redefine |\mark| so that the first page mark either sets the switch to true (in vertical mode all possible special page breaks are already handled) or forces the |\output| routine to do this at an appropriate place. In the last case we can use |\cb_trigger_output| again. Afterwards we restore the original meaning of |\mark| again to reduce the processing overhead (and the dead cycles). This change of |\mark| has the consequence that the first |\mark| in a document cannot be used anymore in horizontal mode inside a vertical box that shall be split afterwards. But this is only sensible if this mark shall be used as |\splitfirstmark| because it will almost never migrate to the outer list---really a rare case! \beginprog \let\cb_mark=\mark \def\mark{% \ifvmode \ifinner \else \global\_cb_save_mark@true \fi % split marks! \else \cb_break_penalty=\mark_penalty_group00 % this will corrupt \vsplit \cb_trigger_output \fi \global\let\mark=\cb_mark \cb_mark } \endprog \sect \begin{newsloppy} If the output routine is triggered with the mark penalty value, it will call {\tt \bs cb\_save\discretionary{\_}{}{}page\_marks}. \end{newsloppy} \beginprog \def\cb_save_page_marks{% % this may be executed twice \unvbox\@cclv \global\_cb_save_mark@true } \endprog \sect To finish the treatment of page marks we can formulate the two macros which are used at the first resp.\ second invocation of a ``special output,'' the principles have already been explained. \beginprog \def\cb_backup_page_marks{% \if_cb_save_mark@ \mark{\topmark}% \xdef\cb_save_firstmark{\firstmark}% \xdef\cb_save_botmark{\botmark}% \fi } \def\cb_restore_page_marks{% \if_cb_save_mark@ \mark{\cb_save_firstmark}\mark{\cb_save_botmark}% \fi } \endprog \chap Positioning the Change Bars. \begin{newsloppy} Now we handle the positions of the bars. The dimension |\cb_bot_change_pos| will hold the position of the end of the change bar, i.e.\ the distance between top of page the end of the changed area. The dimension |\cb_top_change_pos| will hold the beginning of a changed area; a value of |\maxdimen| indicates that no change is in effect. If a changed area is completed, it is appended to the list |\cb_bar_list| as an element {\tt \bs cb\_bar(\bs cb\_top\_change\_pos, \bs cb\_bot\_change\_pos, \bs changebar\-width)}. This list contains all changed areas within the current page so that bars can be written later on. A single bar will be produced by |\cb_write_bar|. \noindent The definition of |\cb_bar| to |\relax| allows the concatenation of new elements to |\cb_bar_list| with |\xdef|. Local dimension register |\halfwidth| is used to center the change bar. \end{newsloppy} \beginprog \newdimen\cb_bot_change_pos \newdimen\cb_top_change_pos \cb_top_change_pos=\maxdimen \let\cb_bar_list=\empty \let\cb_bar=\relax \def\cb_write_bar(#1,#2,#3){{% \dimendef\halfwidth=255% \setbox0=\hbox{\vrule width #3 height -#1 depth #2}% \dp0=0pt \ht0=0pt \wd0=0pt% \halfwidth=#3 \divide\dimen255 by 2% \hskip -\halfwidth% \box0% \hskip \halfwidth% }} \endprog \sect If the output routine was activated by a |\outputpenalty| value within the range of our reserved penalties, the change handling will occur, otherwise standard plain output can be done. There may be a lot of interaction between the routines which set change bars and the output routine. These interactions should not be recorded in |\deadcycles| or else \TeX{} will soon grumble. One might consider doing the same for the|\mark_penalty_group|. But since this group is called once it shouldn't matter all that much. One might also consider adding a counter like |\deadcycles| just to count the change bar interactions as is done for the calls to |\output|, and have {\TeX} grumble if there are ``too many'' of them. For now we don't do this---all of this code is correct anyway! First we save away the old output routine in case the user had redefined this beforehand. \beginprog \newtoks\cb_oldoutput \edef\cb_oldoutput{\the\output} \newcount\penalty_group \output={% \boxmaxdepth=\maxdepth \penalty_group=\outputpenalty \divide \penalty_group by 100 \ifnum \penalty_group=\cb_penalty_group\space {\countdef\new_deadcycles=255% don't count as a dead cycle \new_deadcycles=\deadcycles \advance \new_deadcycles by -1 \deadcycles=\new_deadcycles} \cb_change_handling \else \ifnum \penalty_group=\mark_penalty_group\space \cb_save_page_marks \else \cb_oldoutput \fi \fi } \endprog \sect As explained before, the change handling must differentiate between the kind of the change command (beginning is indicated by $|\cb_change_cmd|=0$, end by~1) and between the mode (horizontal indicated by an odd |\cb_change_mode| value, vertical by an even). A change mode higher than one indicates that we are doing the first page break that has to backup the page as far as it exists already and results in an empty current list of page elements. \beginprog \newcount\cb_change_cmd \newcount\cb_change_mode \def\cb_change_handling{% \cb_change_cmd=-\outputpenalty % ==> absolute value \advance \cb_change_cmd by \cb_penalty_group00 % subtraction \cb_change_mode=\cb_change_cmd \divide \cb_change_cmd by 10 % second-to-last digit \advance \cb_change_mode by -\number\cb_change_cmd0 % last digit \ifnum \cb_change_mode>1 \cb_backup_page \else \ifcase \cb_change_cmd \cb_begin_change \or \cb_end_change \else \errmessage{Invalid changepenalty}% \fi \fi } \endprog \sect Processing a mark during the second trigger of the output routine means restoring the page and storing the positions. At the beginning, the begin of the change is saved, at the end, we know the bar already and put it into the bar list. Then the positioning values are reinitialized. As within every output invocation, the box 255 must be unboxed. As we are here in the second invocation of the output routine the |\box255| consists only of the empty |\vbox| we have inserted in |\cb_trigger_output|. We can therefore throw it away. \beginprog \def\cb_begin_change{% \cb_restore_page \setbox0=\box\@cclv \ifdim \cb_top_change_pos=\maxdimen \global\cb_top_change_pos=\cb_bot_change_pos \global\cb_bot_change_pos=0pt \else \errmessage{Nested change bars are not supported}% \fi } \def\cb_end_change{% \cb_restore_page \setbox0=\box\@cclv \ifdim \cb_top_change_pos=\maxdimen \errmessage{No change is in effect}% \else \xdef\cb_bar_list{\cb_bar_list \cb_bar(\the\cb_top_change_pos,\the\cb_bot_change_pos, \the\cb_changebarwidth)} \global\cb_top_change_pos=\maxdimen \global\cb_bot_change_pos=0pt \fi } \endprog \chap Handling the Page Contents. We handle the part of the page that was collected up to now by putting it into a box. This fixes the position of the change mark so that |\cb_bot_change_pos| can be set and stored in |\cb_top_change_pos| later on or as the lower end of a bar in |\cb_bar_list|. In the first output invocation, we save the contents of the page in |\cb_save_page|. Before that, we store the size of the box (which equals |\pagegoal|!)\ in |\cb_page_goal|. If the unboxing caused an increase of height (i.e.\ if $|\pagetotal|>|\pagegoal|$), we eject the page up to the change mark. Now we have to compute the current position of our mark in |\cb_bot_change_pos|. It is fixed by the size of the |\cb_save_page|, but if the change begins in horizontal mode we must decrease it from the baseline position to the top of the last line. Finally, we must save the values for the allowed insertions and change them to the maximal value so that a rest that is split from an insertion will be appended to the insertion box at the second invocation in every case. The |\vsize| is initialized to |\maxdimen|. This allows to control whether this first output invocation ocurred or if it was discarded. For the same reason |\cb_bot_change_pos| is initialized to~0pt. \beginprog \newbox\cb_save_page \newdimen\cb_page_goal \newdimen\cb_save_vsize \cb_save_vsize=\maxdimen \newdimen\cb_save_dimen_topins \newdimen\cb_save_dimen_footins \def\cb_backup_page{% \global\cb_page_goal=\ht\@cclv \global\setbox\cb_save_page=\vbox{\unvbox\@cclv}% \ifdim \ht\cb_save_page>\cb_page_goal \cb_eject_page_so_far \fi \cb_bot_change_pos=\ht\cb_save_page \global\advance \cb_bot_change_pos by \dp\cb_save_page \ifnum \cb_change_cmd=\cb_penalty_begin\space \ifodd \cb_change_mode \higher_change_pos \fi \fi \global\cb_save_vsize=\vsize \global\vsize=\maxdimen \global\cb_save_dimen_footins=\dimen\footins \global\dimen\footins=\maxdimen \cb_backup_page_marks } \cb_bot_change_pos=0pt \endprog \sect To eject a page as far as it is we restore it from the |\cb_save_page| back to box~255. In horizontal mode and at a begin mark the last line contains the mark and must not be output. So we remove it and the preceding glue from the stored rest, just leaving a single hbox to be on top of the actual page (in |\cb_save_page|) now. Then normal output can be done with box~255. \beginprog \def\cb_eject_page_so_far{% \begingroup \vbadness=20000 % don't complain about underfull vboxes \global\setbox\@cclv=\vbox to \cb_page_goal{% \unvbox\cb_save_page \ifnum \cb_change_cmd=\cb_penalty_begin\space \ifodd \cb_change_mode \global\setbox\cb_save_page=\lastbox \unskip \fi \fi }% \endgroup \cb_oldoutput } \endprog \sect \begin{newsloppy} In horizontal mode and at a begin mark, we need the position of the mark ({\tt \bs cb\_bot\discretionary{\_}{}{}change\_pos}) on the upper boundary of the last line in |\cb_save_page|. If there is just one line left from a recent eject, the height is given by |\topskip| decreased by the height of this hbox. If the height of the box is larger than |\topskip| the skip will not be inserted and the change position results to~0pt. Otherwise, |\cb_save_page| is a vbox whose last hbox we delete temporarily using box~0. Height and depth of the rest are the actual position on the page. \end{newsloppy} The double of the page we have constructed this way will immediately be fed back to the garbage collector because it could have become reasonably large. \beginprog \def\higher_change_pos{% \ifhbox \cb_save_page % rest of page from \cb_eject_page_so_far \cb_bot_change_pos=\topskip \global\advance \cb_bot_change_pos by -\ht\cb_save_page \ifdim \cb_bot_change_pos<0pt \global\cb_bot_change_pos=0pt \fi \else \setbox0=\vbox{% \unvcopy\cb_save_page \setbox0=\lastbox % delete last line }% \cb_bot_change_pos=\ht0 \global\advance \cb_bot_change_pos by \dp0 \setbox0=\box\voidb@x \fi } \endprog \sect To restore a page during the second output invocation, we first restore the saved values, but only if they were really changed (this can be discovered by the value of |\cb_save_vsize|). Now the |\cb_save_page| is appended to the current list as a box, which stops later usage of its stretch- and shrinkability! Then the collected insertions can be inserted again. The page marks have to be inserted, too. \beginprog \def\cb_restore_page{% \ifdim \cb_save_vsize=\maxdimen \else \global\vsize=\cb_save_vsize \global\dimen\footins=\cb_save_dimen_footins \global\cb_save_vsize=\maxdimen \cb_restore_page_marks \fi \box\cb_save_page % discards stretch- and shrinkability! \ifvoid \footins \else \insert\footins{\floatingpenalty=20000 \unvbox\footins}% \fi } \endprog \sect {\em Please note, that there is still a problem with this concept of handling the output trigger:} \bigskip If the first output trigger is discarded because a page break has occurred just in front, footnote parts may be juggled around. I.e., if a footnote is split in three parts, the first part was just been shipped out, the second part is inserted back into the recent contributions by the output routine but {\em behind\/} the third part which is saved in the ``special place'' (according to the \TeX{}book, p.~125). A solution to this problem might be to insert a |\do_change| again within the second output trigger and finishing the treatment afterwards. Afterwards a full triggering process (two output invocations) is executed again and all insertion parts will be accessible in the insertion box. By the way, the almost same problem appears in \LaTeX{}, too. Almost: in \LaTeX{} this can happen every time because at the first output invocation the |\dimen|-values of the footnote insertion is not increased. I leave the problem open to the reader\,\dots \chap Writing the Stuff. The positions of the bars which mark the changed areas are relative to the top of the text, i.e.\ the height of the top insertion is not included. Therefore it is best to write them just after the top insertions before the page text---but to do this we have to change the \beginchange either the {\sc Plain} {\TeX} macro |\pagecontents| or the {\LaTeX} macro |\@makecol|. \endchange Below is the new definition, I have just rearranged it a little bit so that it is more legible. The new lines have been marked with `|%%%%|'. |\cb_insert_current_bar| inserts a last element in |\cb_bar_list| if a changed area is not yet finished, afterwards all bars can be written. \beginprog \def\@makecol{% \ifvoid\footins \setbox\@outputbox \vbox{ %\typeout{write all bars -- no footins} \cb_insert_current_bar \cb_write_all_bars %%%% \unvbox\@cclv% } \else \setbox\@outputbox \vbox{\boxmaxdepth \maxdepth %\typeout{write all bars} \cb_insert_current_bar \cb_write_all_bars %%%% \unvbox\@cclv\vskip\skip\footins\footnoterule\unvbox\footins% } \fi% \xdef\@freelist{\@freelist\@midlist}\gdef\@midlist{}\@combinefloats \setbox\@outputbox\vbox to\@colht{\boxmaxdepth\maxdepth \@texttop\dimen128=\dp\@outputbox\unvbox\@outputbox \vskip-\dimen128\@textbottom}% \global\maxdepth\@maxdepth} \endprog \sect If $|\cb_top_change_pos|=|\maxdimen|$ no change is active. Otherwise the current change reaches from the begin mark (|\cb_top_change_pos|) to the end of the page, i.e.\ we insert a virtual end mark. Because the change continues on the next page we insert a virtual begin mark on the top of the page, too. \beginprog \def\cb_insert_current_bar{% \ifdim \cb_top_change_pos=\maxdimen \else% \cb_bot_change_pos=\ht\@cclv \advance\cb_bot_change_pos by \dp\@cclv \xdef\cb_bar_list{\cb_bar_list \cb_bar(\the\cb_top_change_pos, \the\cb_bot_change_pos, \the\changebarwidth)}% \global\cb_top_change_pos=0pt \fi% } \endprog \sect Now we can write all bars---if they exist anyway. It's rather easy, we just have to define |\cb_bar| to |\cb_write_bar| and execute |\cb_bar_list|. The resulting output must not use vertical place. We must not forget to delete the list, or we will get the same bars on the next page again. \beginprog \newbox\cb_bars \newdimen\cb_offset \def\cb_write_all_bars{% \ifx \cb_bar_list\empty \else % changes exist \ifchbarRight \cb_offset = \hsize \advance \cb_offset by \BarDistance \else \cb_offset = \hoffset \advance \cb_offset by -\BarDistance \fi \setbox\cb_bars=\hbox to \cb_offset{% \hskip\cb_offset \vbox to 0pt{\offinterlineskip \let\cb_bar=\cb_write_bar \cb_bar_list }% \hss }% \ht\cb_bars=0pt \dp\cb_bars=0pt \box\cb_bars \global\let\cb_bar_list=\empty \fi } \endprog \beginchange\chap Cleaning Up.\endchange We finish the macro file so that garbage (e.g.\ of exchanges between systems) can come afterwards. \beginprog \catcode`\@=\atcode \catcode`\_=\uscode \endinput \endprog \end{document}