1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
|
% 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}
|