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
|
extern queue squeue; // the three queues on which ranges reside
extern queue bfqueue;
extern queue ufqueue;
extern double minfull;
extern double coltol;
int anymore();
// The following is used in some calls to range::enqueue(int = 0).
#define ANDBLOCK 1
class page;
enum { DRAFT = 0, FINAL = 1 };
// The mergestream currpage->stage serves as a staging area for page makeup:
// when primed, it contains a minimal acceptable chunk of input ranges.
// The page must either take or leave everything that's on stage.
class mergestream : public queue {
page *currpage; // current page that's accepting stuff
public:
mergestream(page *cp) { currpage = cp; unblock(); }
void unblock();
int prime(); // stage next legal chunk
void pend(); // process pending chunk on stage
};
// The multicol currpage->twocol is the two-column piece of the page to which
// two-column ranges are currently being added.
// The page sets htavail to indicate how tall it is allowed to become.
// All ranges on definite must be placed when the multicol is printed.
// Each of these definite ranges also resides on one of column[0] and [1],
// which represent the current best guess about how to divide definite
// between the two columns.
class multicol : public range {
page *currpage; // current page that's accepting stuff
stream definite; // definitely on page
stream scratch; // for trial compositions
stream column[2]; // left (0) and right (1) columns
int leftblocked; // OK to add to left column?
int htavail; // max possible ht, set by page::tryout()
int prevhtavail; // max 2-colht last time we added something
friend class page;
public:
multicol(page *cp) { currpage = cp;
leftblocked = 0;
htavail = 0;
prevhtavail = -1;
setgoal(NOGOAL); }
// the two-column piece behaves as part
// of the stream of single-column input.
int numcol() { return 1; }
int nonempty() { return definite.more(); }
void choosecol(range *, int);// add first arg to one or other column
void choosecol(stream*, int);// add *all ranges on first arg*
// to one or other column
// NOT the same as a mapcar of the
// preceding function over the ranges
// on the first argument!
void compose(int); // divide into two columns
void tryout(); // decide which column gets stage contents
void stretch(int); // justify both columns to given height
int print(int curv, int col);
int height(); // an upper bound on actual height
int rawht() { return max(column[0].rawht(), column[1].rawht()); }
void reheight(int *cv, int *mv)
{ *cv += height(); *mv = max(*mv, *cv); }
void dump();
int isvbox() { return nonempty(); } // during trimspace()
};
// These sentinel ranges are used to separate the ranges on twocol::definite
// into the chunks in which they came from the staging area.
// Thus, they preserve the results of the computation that was done to prime
// page::stage.
class sentrange : public range {
public:
sentrange() { }
int numcol() { return 2; }
int issentinel() { return 1; }
};
class page {
int pagesize; // allowed maximum height
int prevncol; // was last item tried 1- or 2-column?
int vsince; // how many vboxes from "current" BS
// (to avoid putting a single line on
// a page with a very large floatable)
stream definite; // definitely on page, in input order
stream scratch; // playground in which to alter page
void cmdproc(); // process any of several commands
void parmproc(); // process any of several parameters
void tryout(); // see whether current stage contents fit
void compose(int); // float and trim current page contents
void makescratch(int); // fill scratch area
void commit(); // accept the items on stage
void welsh(); // reject the items on stage
void adddef(range *r); // add to one of the definite queues
// (definite or twocol->definite)
public:
mergestream *stage;
friend class mergestream;
multicol *twocol;
friend class multicol;
page(int p) { pagesize = p;
prevncol = 1;
vsince = 0;
stage = new mergestream(this);
twocol = new multicol(this); }
~page() { definite.freeall(); scratch.freeall(); }
void fill();
int blank() { return !definite.more() && !twocol->definite.more();}
void print();
};
// functions in page.c
int main(int, char **);
|