% 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}