From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/plain/contrib/chbar/chbars.doc | 738 ++++++++++++++++++++++++++++++++++ 1 file changed, 738 insertions(+) create mode 100644 macros/plain/contrib/chbar/chbars.doc (limited to 'macros/plain/contrib/chbar/chbars.doc') diff --git a/macros/plain/contrib/chbar/chbars.doc b/macros/plain/contrib/chbar/chbars.doc new file mode 100644 index 0000000000..77610d2db3 --- /dev/null +++ b/macros/plain/contrib/chbar/chbars.doc @@ -0,0 +1,738 @@ +% This is CHBARS.DOC as of 09 Oct 89 +%--------------------------------------------------------- +% (c) 1989 by J.Schrod. copy conditions see below. + +% +% Macro package for creating changebars in Plain TeX +% MAKEPROG is needed +% +% documented in LaTeX (for Anne and Chris) + +% +% VERSION HISTORY (MSCF -- most significant change first) +% +% DATE PERSON REMARK +% 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... +\documentstyle[progltx,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} + + +\title{Changebars without {\tt \bs{}special}'s} +\author{\sc Joachim Schrod} + +\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 Plain~\TeX{} but the implementation +strategy can be used with \LaTeX{}, too. +\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 no strechability or shrinkability on a page on top of the last +mark of a change is supported. 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 titlepages or for similar +constructions. Changes in floating insertions (footnotes, figures) are +not discovered. + +The demonstrated solution is written in Plain \TeX{}, because it was +easier and could be presented better at the Euro\TeX89 conference in +Karlsruhe. An adaption to \LaTeX{} is possible, but would require +modifications to the \LaTeX{} kernel, i.e.\ the output routine and the +layout parameters of the standard styles. + + +\sect 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~1, 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, +If you have not received a copy of the \GNU{} General Public License +along with this program, write to the Free Software Foundation, +Inc., 675~Mass Ave, Cambridge, MA~02139, USA. + + +\sect A changed area is described by two marks, |\beginchange| and +|\endchange|. The method for writing a change bar consists of three +parts: First, it will be indicated to the output routine that a change +has occured. Next, the position of the changed area will be found out +and fixed. A list of all changed areas is built, that can at last be +marked by the output routine using vertical rules at the right margin. + +But before we start 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|.) +With the at sign we can use the ``private'' Plain macros and with the +underscore we can make our own macros more readable. But as we have to +restore these category codes at the end of this macro file we store +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. + +\beginprog +\chardef\letter=11 + +\chardef\atcode=\catcode`\@ +\chardef\uscode=\catcode`\_ + +\catcode`\@=\letter +\catcode`\_=\letter +\endprog + + + + +\chap Triggering the Output Routine. + +\begin{sloppypar} +First, the output routine must know that changes occured. This is done +by the macros |\beginchange| and |\endchange| that append a reserved +penalty value |\break_penalty| to the current list that is below +$-10\,000$. The penalty will be in the range $|\change_penalty_group| +\cdot 100 - 99 \ldots |\change_penalty_group| \cdot 100$. +For the output it must be differentiated if the mark indicates +begin or end of the changed area. This is done with the values +|\change_penalty_begin| and |\change_penalty_end| that are used as the +second-to-last digit of the change penalty. +\end{sloppypar} + +It must also be differentiated between the beginning of a mark being in +horizontal or vertical mode. In horizontal mode, the change bar must +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 the look-ahead for digits. + +These interactions with the output routine adds a lot of dead cycles; we +must therefore increase |\maxdeadcycles| to prevent \TeX{} from grumbling. + +\beginprog +\def\change_penalty_group{-101} +\def\change_penalty_begin{0} +\def\change_penalty_end{1} + +\maxdeadcycles=100 +\endprog + + +\sect The marks set the change penalty value (including the indication +of begin or end of mark). The rest (|\trigger_output|) is the same action +for both. An end change mark in horizontal mode can be preceeded by +glue that could cause a line break, thus including the following line to +the change area as well. To avoid this unwanted behaviour, the space is +saved in |\save_space|, discarded in front of the mark and restored +afterwards. + +\beginprog +\newcount\break_penalty +\newskip\save_space + +\def\beginchange{% + \break_penalty=\change_penalty_group\change_penalty_begin0 + \trigger_output + } +\def\endchange{% + \ifhmode \save_space=\lastskip \unskip \fi + \break_penalty=\change_penalty_group\change_penalty_end0 + \trigger_output + \ifhmode \hskip\save_space \fi + } +\endprog + + +\sect In |\trigger_output| the real output trigger is done. We must consider +that an |\output|-invocation by our |\break_penalty| can be discarded 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. + +Now 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. + +\beginprog +\newcount\save_space_factor + +\def\trigger_output{% + \ifinner \errmessage{Change cannot be marked inside a box}% + \else + \ifvmode \let\do_in_vmode=\relax + \advance \break_penalty by -2 + \else \save_space_factor=\spacefactor + \let\do_in_vmode=\vadjust + \advance \break_penalty by -3 + \fi + \do_in_vmode{% + \penalty\break_penalty + \null + \advance \break_penalty by 2 \penalty\break_penalty + }% + \ifhmode \spacefactor\save_space_factor \fi + \fi + } +\endprog + + +\sect The usage of the output routine as an information passing system +has always it's difficulties. One of the real hard parts is the +handling of the page marks, special token lists that are handled by +\TeX{}. These token lists provide informations about text on a page, +they are usually used to create head lines etc. The user may access +these token lists with 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 have luck: 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 |\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@save_mark@ \@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 |\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\@@mark=\mark +\def\mark{% + \ifvmode + \ifinner \else \global\@save_mark@true \fi % split marks! + \else \break_penalty=\mark_penalty_group00 % this will corrupt \vsplit + \trigger_output + \fi + \global\let\mark=\@@mark + \@@mark + } +\endprog + + +\sect +\begin{sloppypar} +If the output routine is triggered with the mark penalty value +it will call |\start_saving_page_marks|. +\end{sloppypar} + +\beginprog +\def\start_saving_page_marks{% % this may be executed twice + \unvbox255 + \global\@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\backup_page_marks{% + \if@save_mark@ + \mark{\topmark}% + \xdef\save_firstmark{\firstmark}% + \xdef\save_botmark{\botmark}% + \fi + } + +\def\restore_page_marks{% + \if@save_mark@ + \mark{\save_firstmark}\mark{\save_botmark}% + \fi + } +\endprog + + + + + +\chap Positioning the Change Marks. + +Now we must handle the positions of the bars. |\change_pos| will hold +the position of the actual mark, i.e.\ the distance between top of page +and actual mark. |\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 |\bar_list| as +an element |\bar(\top_change_pos,\change_pos)|. 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 |\write_bar|. + +\noindent The definition of |\bar| to |\relax| allows the concatenation +of new elements to |\bar_list| with |\xdef|. + +\beginprog +\newdimen\change_pos +\newdimen\top_change_pos \top_change_pos=\maxdimen + +\let\bar_list=\empty + +\let\bar=\relax +\def\write_bar(#1,#2){% + \setbox0=\hbox{\vrule height -#1 depth #2}% + \dp0=0pt \ht0=0pt + \box0 + } +\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. + +\beginprog +\newcount\penalty_group + +\output={% + \boxmaxdepth=\maxdepth + \penalty_group=\outputpenalty \divide \penalty_group by 100 + \ifnum \penalty_group=\change_penalty_group\space \change_handling + \else + \ifnum \penalty_group=\mark_penalty_group\space + \start_saving_page_marks + \else \plainoutput + \fi + \fi + } +\endprog + + +\sect As explained before, the change handling must differentiate +between the kind of the change command (beginning is indicated by +$|\change_cmd|=0$, end by~1) and between the mode (horizontal indicated +by an odd |\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\change_cmd +\newcount\change_mode + +\def\change_handling{% + \change_cmd=-\outputpenalty % ==> absolute value + \advance \change_cmd by \change_penalty_group00 % subtraction + \change_mode=\change_cmd + \divide \change_cmd by 10 % second-to-last digit + \advance \change_mode by -\number\change_cmd0 % last digit + \ifnum \change_mode>1 \backup_page + \else + \ifcase \change_cmd \begin_change + \or \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 +|\trigger_output|. We can therefore throw it away. + +\beginprog +\def\begin_change{% + \restore_page + \setbox0=\box255 + \ifdim \top_change_pos=\maxdimen + \global\top_change_pos=\change_pos \global\change_pos=0pt + \else \errmessage{Nested change bars are not supported}% + \fi + } + +\def\end_change{% + \restore_page + \setbox0=\box255 + \ifdim \top_change_pos=\maxdimen + \errmessage{No change is in effect}% + \else + \xdef\bar_list{\bar_list \bar(\the\top_change_pos,\the\change_pos)}% + \global\top_change_pos=\maxdimen \global\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 +|\change_pos| can be set and stored later on in |\top_change_pos| or as +the lower end of a bar in |\bar_list|. + +To save the page contents in the first output invocation, we have to +save the page in |\save_page|. Before that, we store the size of the +box (which equals |\pagegoal|!)\ in |\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 |\change_pos|. It is fixed by the size +of the |\save_page|, but in case of a begin mark 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 |\change_pos| is initialized to~0pt. + +\beginprog +\newbox\save_page + +\newdimen\page_goal +\newdimen\save_vsize \save_vsize=\maxdimen +\newdimen\save_dimen_topins +\newdimen\save_dimen_footins + +\def\backup_page{% + \global\page_goal=\ht255 + \global\setbox\save_page=\vbox{\unvbox255}% + \ifdim \ht\save_page>\page_goal \eject_page_so_far \fi + \change_pos=\ht\save_page \global\advance \change_pos by \dp\save_page + \ifnum \change_cmd=\change_penalty_begin\space + \ifodd \change_mode \higher_change_pos \fi + \fi + \global\save_vsize=\vsize \global\vsize=\maxdimen + \global\save_dimen_topins=\dimen\topins \global\dimen\topins=\maxdimen + \global\save_dimen_footins=\dimen\footins + \global\dimen\footins=\maxdimen + \backup_page_marks + } + +\change_pos=0pt +\endprog + + +\sect To eject a page as far as it is we restore it from the +|\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 |\save_page|) now. Then normal +output can be done with box~255. + +\beginprog +\def\eject_page_so_far{% + \begingroup + \vbadness=20000 % don't complain about underfull vboxes + \global\setbox255=\vbox to \page_goal{% + \unvbox\save_page + \ifnum \change_cmd=\change_penalty_begin\space + \ifodd \change_mode + \global\setbox\save_page=\lastbox + \unskip + \fi + \fi + }% + \endgroup + \plainoutput + } +\endprog + + +\sect +\begin{sloppypar} +In horizontal mode and at a begin mark, we need the position of +the mark (|\change_pos|) on the upper boundary of the last line in +|\save_page|. If there is just one line left from a recent eject, the +height is given by the 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, +|\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{sloppypar} + +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 \save_page % rest of page from \eject_page_so_far + \change_pos=\topskip \global\advance \change_pos by -\ht\save_page + \ifdim \change_pos<0pt \global\change_pos=0pt \fi + \else + \setbox0=\vbox{% + \unvcopy\save_page + \setbox0=\lastbox % delete last line + }% + \change_pos=\ht0 \global\advance \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 |\save_vsize|). Now the |\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\restore_page{% + \ifdim \save_vsize=\maxdimen + \else \global\vsize=\save_vsize + \global\dimen\topins=\save_dimen_topins + \global\dimen\footins=\save_dimen_footins + \global\save_vsize=\maxdimen + \restore_page_marks + \fi + \box\save_page % discards stretch- and shrinkability! + \ifvoid \topins + \else \insert\topins{\floatingpenalty=0 \unvbox\topins}% + \fi + \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 occured +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 treatmend afterwards. Afterwards a +full triggering process (two output invocations) is executed again and +alle 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 +Plain macro |\pagecontents|. + +Below is the new definition, I have just rearranged it a little bit so +that it is more legible. The line I have inserted is marked with +`|%%%%|'. |\insert_current_bar| inserts a last element in |\bar_list| +if a changed area is not yet finished, afterwards all bars can be +written. + +\beginprog +\def\pagecontents{% + \ifvoid \topins \else \unvbox\topins \fi + \insert_current_bar \write_all_bars %%%% + \dimen@=\dp\@cclv \unvbox\@cclv % open up \box255 + \ifvoid \footins + \else % footnote info is present + \vskip\skip\footins + \footnoterule + \unvbox\footins + \fi + \ifr@ggedbottom \kern-\dimen@ \vfil \fi + } +\endprog + + +\sect If $|\top_change_pos|=|\maxdimen|$ no change is active. Otherwise +the current change reaches from the begin mark (|\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\insert_current_bar{% + \ifdim \top_change_pos=\maxdimen + \else + \change_pos=\ht255 \advance\change_pos by \dp255 + \xdef\bar_list{\bar_list \bar(\the\top_change_pos,\the\change_pos)}% + \global\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 |\bar| to |\write_bar| and execute +|\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. + +\noindent |\BarDistance| is the amount of place between the text margin +and the change bars. + +\beginprog +\newbox\@bars +\newdimen\BarDistance \BarDistance=2cc + +\def\write_all_bars{% + \ifx \bar_list\empty + \else % changes exist + \setbox\@bars=\hbox to \hsize{% + \hskip\hsize \hskip\BarDistance + \vbox to 0pt{\offinterlineskip + \let\bar=\write_bar \bar_list + }% + \hss + }% + \ht\@bars=0pt \dp\@bars=0pt \box\@bars + \global\let\bar_list=\empty + \fi + } +\endprog + + +\sect 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} -- cgit v1.2.3