Blame


1 5f1cf8e6 2004-05-16 devnull #include "misc.h"
2 5f1cf8e6 2004-05-16 devnull #include "slug.h"
3 5f1cf8e6 2004-05-16 devnull #include "range.h"
4 5f1cf8e6 2004-05-16 devnull #include "page.h"
5 5f1cf8e6 2004-05-16 devnull
6 5f1cf8e6 2004-05-16 devnull const int MAXRANGES = 1000;
7 5f1cf8e6 2004-05-16 devnull static range *ptemp[MAXRANGES]; // for movefloats()
8 5f1cf8e6 2004-05-16 devnull
9 5f1cf8e6 2004-05-16 devnull static void swapright(int n) // used by movefloats()
10 5f1cf8e6 2004-05-16 devnull {
11 5f1cf8e6 2004-05-16 devnull range *t = ptemp[n];
12 5f1cf8e6 2004-05-16 devnull ptemp[n] = ptemp[n+1];
13 5f1cf8e6 2004-05-16 devnull ptemp[n+1] = t;
14 5f1cf8e6 2004-05-16 devnull ptemp[n]->setaccum( ptemp[n+1]->accum() -
15 5f1cf8e6 2004-05-16 devnull ptemp[n+1]->rawht() + ptemp[n]->rawht() );
16 5f1cf8e6 2004-05-16 devnull ptemp[n+1]->setaccum( ptemp[n]->accum() + ptemp[n+1]->rawht() );
17 5f1cf8e6 2004-05-16 devnull }
18 5f1cf8e6 2004-05-16 devnull
19 5f1cf8e6 2004-05-16 devnull // Figure out the goal position for each floating range on scratch,
20 5f1cf8e6 2004-05-16 devnull // and move it past stream ranges until it's as close to its goal as possible.
21 5f1cf8e6 2004-05-16 devnull static void movefloats(stream *scratch, double scale)
22 5f1cf8e6 2004-05-16 devnull {
23 5f1cf8e6 2004-05-16 devnull int nranges, i;
24 5f1cf8e6 2004-05-16 devnull const int Huge = 100000;
25 5f1cf8e6 2004-05-16 devnull
26 5f1cf8e6 2004-05-16 devnull for (nranges = 0; scratch->more(); scratch->advance())
27 5f1cf8e6 2004-05-16 devnull ptemp[nranges++] = scratch->current();
28 5f1cf8e6 2004-05-16 devnull scratch->freeall();
29 5f1cf8e6 2004-05-16 devnull ufrange rtemp;
30 5f1cf8e6 2004-05-16 devnull ptemp[nranges] = &rtemp;
31 5f1cf8e6 2004-05-16 devnull rtemp.setgoal(Huge);
32 5f1cf8e6 2004-05-16 devnull int accumV = 0; // compute accum values and
33 5f1cf8e6 2004-05-16 devnull for (i = 0; i < nranges; i++) { // pick closest goal for floats
34 5f1cf8e6 2004-05-16 devnull ptemp[i]->pickgoal(accumV, scale);
35 5f1cf8e6 2004-05-16 devnull ptemp[i]->setaccum(accumV += ptemp[i]->rawht());
36 5f1cf8e6 2004-05-16 devnull }
37 5f1cf8e6 2004-05-16 devnull int j; // index for inner loop below:
38 5f1cf8e6 2004-05-16 devnull for (i = nranges; --i >= 0; ) // stably sort floats to bottom
39 5f1cf8e6 2004-05-16 devnull for (j = i; j < nranges; j++)
40 5f1cf8e6 2004-05-16 devnull if (ptemp[j]->goal() > ptemp[j+1]->goal())
41 5f1cf8e6 2004-05-16 devnull swapright(j);
42 5f1cf8e6 2004-05-16 devnull else
43 5f1cf8e6 2004-05-16 devnull break;
44 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
45 5f1cf8e6 2004-05-16 devnull printf("#movefloats: before floating, from bottom:\n");
46 5f1cf8e6 2004-05-16 devnull for (i = nranges; --i >= 0; ) { // find topmost float
47 5f1cf8e6 2004-05-16 devnull if (ptemp[i]->goal() == NOGOAL)
48 5f1cf8e6 2004-05-16 devnull break;
49 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
50 5f1cf8e6 2004-05-16 devnull printf("# serialno %d goal %d height %d\n",
51 5f1cf8e6 2004-05-16 devnull ptemp[i]->serialno(), ptemp[i]->goal(),
52 5f1cf8e6 2004-05-16 devnull ptemp[i]->rawht());
53 5f1cf8e6 2004-05-16 devnull } // i+1 is topmost float
54 5f1cf8e6 2004-05-16 devnull for (i++ ; i < nranges; i++) // move each float up the page
55 5f1cf8e6 2004-05-16 devnull for (j = i; j > 0; j--) // as long as closer to its goal
56 5f1cf8e6 2004-05-16 devnull if (ptemp[j]->goal()
57 5f1cf8e6 2004-05-16 devnull <= ptemp[j-1]->accum() + ptemp[j]->rawht()/2
58 5f1cf8e6 2004-05-16 devnull && ptemp[j-1]->goal() == NOGOAL)
59 5f1cf8e6 2004-05-16 devnull swapright(j-1);
60 5f1cf8e6 2004-05-16 devnull else
61 5f1cf8e6 2004-05-16 devnull break;
62 5f1cf8e6 2004-05-16 devnull if (ptemp[nranges] != &rtemp)
63 5f1cf8e6 2004-05-16 devnull ERROR "goal sentinel has disappeared from movefloats" FATAL;
64 5f1cf8e6 2004-05-16 devnull for (i = 0; i < nranges; i++) // copy sorted list back
65 5f1cf8e6 2004-05-16 devnull scratch->append(ptemp[i]);
66 5f1cf8e6 2004-05-16 devnull }
67 5f1cf8e6 2004-05-16 devnull
68 5f1cf8e6 2004-05-16 devnull // Traverse the leaves of a tree of ranges, filtering out only SP and VBOX.
69 5f1cf8e6 2004-05-16 devnull static range *filter(generator *g)
70 5f1cf8e6 2004-05-16 devnull {
71 5f1cf8e6 2004-05-16 devnull range *r;
72 5f1cf8e6 2004-05-16 devnull while ((r = g->next()) != 0)
73 5f1cf8e6 2004-05-16 devnull if (r->isvbox() || r->issp())
74 5f1cf8e6 2004-05-16 devnull break;
75 5f1cf8e6 2004-05-16 devnull return r;
76 5f1cf8e6 2004-05-16 devnull }
77 5f1cf8e6 2004-05-16 devnull
78 5f1cf8e6 2004-05-16 devnull // Zero out leading and trailing spaces; coalesce adjacent SP's.
79 5f1cf8e6 2004-05-16 devnull static void trimspace(stream *scratch)
80 5f1cf8e6 2004-05-16 devnull {
81 5f1cf8e6 2004-05-16 devnull generator g;
82 5f1cf8e6 2004-05-16 devnull range *r, *prevr = 0;
83 5f1cf8e6 2004-05-16 devnull
84 5f1cf8e6 2004-05-16 devnull for (g = scratch; (r = filter(&g)) != 0 && r->issp(); prevr = r)
85 5f1cf8e6 2004-05-16 devnull r->setheight(0); // zap leading SP
86 5f1cf8e6 2004-05-16 devnull for ( ; (r = filter(&g)) != 0; prevr = r)
87 5f1cf8e6 2004-05-16 devnull if (r->issp())
88 5f1cf8e6 2004-05-16 devnull if (prevr && prevr->issp()) {
89 5f1cf8e6 2004-05-16 devnull // coalesce adjacent SPs
90 5f1cf8e6 2004-05-16 devnull r->setheight(max(r->rawht(), prevr->height()));
91 5f1cf8e6 2004-05-16 devnull prevr->setheight(0);
92 5f1cf8e6 2004-05-16 devnull } else // a VBOX intervened
93 5f1cf8e6 2004-05-16 devnull r->setheight(r->rawht());
94 5f1cf8e6 2004-05-16 devnull if (prevr && prevr->issp()) // zap *all* trailing space
95 5f1cf8e6 2004-05-16 devnull prevr->setheight(0); // (since it all coalesced
96 5f1cf8e6 2004-05-16 devnull // into the last one)
97 5f1cf8e6 2004-05-16 devnull }
98 5f1cf8e6 2004-05-16 devnull
99 5f1cf8e6 2004-05-16 devnull // Pad the non-zero SP's in scratch so the total height is wantht.
100 5f1cf8e6 2004-05-16 devnull // Note that the SP values in scratch are not the raw values, and
101 5f1cf8e6 2004-05-16 devnull // indeed may already have been padded.
102 5f1cf8e6 2004-05-16 devnull static void justify(stream *scratch, int wantht)
103 5f1cf8e6 2004-05-16 devnull {
104 5f1cf8e6 2004-05-16 devnull range *r;
105 5f1cf8e6 2004-05-16 devnull int nsp = 0, hsp = 0;
106 5f1cf8e6 2004-05-16 devnull
107 5f1cf8e6 2004-05-16 devnull int adjht = scratch->height();
108 5f1cf8e6 2004-05-16 devnull // Find all the spaces.
109 5f1cf8e6 2004-05-16 devnull generator g;
110 5f1cf8e6 2004-05-16 devnull for (g = scratch; (r = g.next()) != 0; )
111 5f1cf8e6 2004-05-16 devnull if (r->issp() && r->height() > 0) {
112 5f1cf8e6 2004-05-16 devnull nsp++;
113 5f1cf8e6 2004-05-16 devnull hsp += r->height();
114 5f1cf8e6 2004-05-16 devnull }
115 5f1cf8e6 2004-05-16 devnull int excess = wantht - adjht;
116 5f1cf8e6 2004-05-16 devnull if (excess < 0)
117 5f1cf8e6 2004-05-16 devnull ERROR "something on page %d is oversize by %d\n",
118 5f1cf8e6 2004-05-16 devnull userpn, -excess WARNING;
119 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
120 5f1cf8e6 2004-05-16 devnull printf("# justify %d: excess %d nsp %d hsp %d adjht %d\n",
121 5f1cf8e6 2004-05-16 devnull userpn, excess, nsp, hsp, adjht);
122 5f1cf8e6 2004-05-16 devnull if (excess <= 0 || nsp == 0)
123 5f1cf8e6 2004-05-16 devnull return;
124 5f1cf8e6 2004-05-16 devnull // Redistribute the excess space.
125 5f1cf8e6 2004-05-16 devnull for (g = scratch; (r = g.next()) != 0; )
126 5f1cf8e6 2004-05-16 devnull if (r->issp() && r->height() > 0) {
127 5f1cf8e6 2004-05-16 devnull int delta = (int) ((float)(r->height()*excess)/hsp + 0.5);
128 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
129 5f1cf8e6 2004-05-16 devnull printf("# pad space %d by %d: hsp %d excess %d\n",
130 5f1cf8e6 2004-05-16 devnull r->height(), delta, hsp, excess);
131 5f1cf8e6 2004-05-16 devnull r->setheight(r->height() + delta);
132 5f1cf8e6 2004-05-16 devnull }
133 5f1cf8e6 2004-05-16 devnull }
134 5f1cf8e6 2004-05-16 devnull
135 5f1cf8e6 2004-05-16 devnull // If r were added to s, would the height of the composed result be at most maxht?
136 5f1cf8e6 2004-05-16 devnull int wouldfit(range *r, stream *s, int maxht)
137 5f1cf8e6 2004-05-16 devnull {
138 5f1cf8e6 2004-05-16 devnull if (r->rawht() + s->rawht() <= maxht)
139 5f1cf8e6 2004-05-16 devnull return 1; // the conservative test succeeded
140 5f1cf8e6 2004-05-16 devnull stream scratch; // local playground for costly test
141 5f1cf8e6 2004-05-16 devnull for (stream cd = *s; cd.more(); cd.advance())
142 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
143 5f1cf8e6 2004-05-16 devnull scratch.append(r);
144 5f1cf8e6 2004-05-16 devnull movefloats(&scratch, ((double) scratch.rawht())/maxht);
145 5f1cf8e6 2004-05-16 devnull trimspace(&scratch);
146 5f1cf8e6 2004-05-16 devnull int retval = scratch.height() <= maxht;
147 5f1cf8e6 2004-05-16 devnull scratch.freeall();
148 5f1cf8e6 2004-05-16 devnull return retval;
149 5f1cf8e6 2004-05-16 devnull }
150 5f1cf8e6 2004-05-16 devnull
151 5f1cf8e6 2004-05-16 devnull // If s1 were added to s, would the height of the composed result be at most maxht?
152 5f1cf8e6 2004-05-16 devnull // The computational structure is similar to that above.
153 5f1cf8e6 2004-05-16 devnull int wouldfit(stream *s1, stream *s, int maxht)
154 5f1cf8e6 2004-05-16 devnull {
155 5f1cf8e6 2004-05-16 devnull if (s1->rawht() + s->rawht() <= maxht)
156 5f1cf8e6 2004-05-16 devnull return 1;
157 5f1cf8e6 2004-05-16 devnull stream scratch, cd;
158 5f1cf8e6 2004-05-16 devnull for (cd = *s; cd.more(); cd.advance())
159 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
160 5f1cf8e6 2004-05-16 devnull for (cd = *s1; cd.more(); cd.advance())
161 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
162 5f1cf8e6 2004-05-16 devnull movefloats(&scratch, ((double) scratch.rawht())/maxht);
163 5f1cf8e6 2004-05-16 devnull trimspace(&scratch);
164 5f1cf8e6 2004-05-16 devnull int retval = scratch.height() <= maxht;
165 5f1cf8e6 2004-05-16 devnull scratch.freeall();
166 5f1cf8e6 2004-05-16 devnull return retval;
167 5f1cf8e6 2004-05-16 devnull }
168 5f1cf8e6 2004-05-16 devnull
169 5f1cf8e6 2004-05-16 devnull // All of stream *s is destined for one column or the other; which is it to be?
170 5f1cf8e6 2004-05-16 devnull void multicol::choosecol(stream *s, int goalht)
171 5f1cf8e6 2004-05-16 devnull {
172 5f1cf8e6 2004-05-16 devnull stream *dest;
173 5f1cf8e6 2004-05-16 devnull if (!leftblocked && wouldfit(s, &(column[0]), goalht))
174 5f1cf8e6 2004-05-16 devnull dest = &(column[0]);
175 5f1cf8e6 2004-05-16 devnull else {
176 5f1cf8e6 2004-05-16 devnull dest = &(column[1]);
177 5f1cf8e6 2004-05-16 devnull if (!s->current()->floatable())
178 5f1cf8e6 2004-05-16 devnull // a stream item is going into the right
179 5f1cf8e6 2004-05-16 devnull // column, so no more can go into the left.
180 5f1cf8e6 2004-05-16 devnull leftblocked = 1;
181 5f1cf8e6 2004-05-16 devnull }
182 5f1cf8e6 2004-05-16 devnull for (stream cd = *s; cd.more(); cd.advance())
183 5f1cf8e6 2004-05-16 devnull dest->append(cd.current());
184 5f1cf8e6 2004-05-16 devnull }
185 5f1cf8e6 2004-05-16 devnull
186 5f1cf8e6 2004-05-16 devnull double coltol = 0.5;
187 5f1cf8e6 2004-05-16 devnull
188 5f1cf8e6 2004-05-16 devnull // Try, very hard, to put everything in the multicol into two columns
189 5f1cf8e6 2004-05-16 devnull // so that the total height is at most htavail.
190 5f1cf8e6 2004-05-16 devnull void multicol::compose(int defonly)
191 5f1cf8e6 2004-05-16 devnull {
192 5f1cf8e6 2004-05-16 devnull if (!nonempty()) {
193 5f1cf8e6 2004-05-16 devnull setheight(0);
194 5f1cf8e6 2004-05-16 devnull return;
195 5f1cf8e6 2004-05-16 devnull }
196 5f1cf8e6 2004-05-16 devnull scratch.freeall(); // fill scratch with everything destined
197 5f1cf8e6 2004-05-16 devnull // for either column
198 5f1cf8e6 2004-05-16 devnull stream cd;
199 5f1cf8e6 2004-05-16 devnull for (cd = definite; cd.more(); cd.advance())
200 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
201 5f1cf8e6 2004-05-16 devnull if (!defonly)
202 5f1cf8e6 2004-05-16 devnull for (cd = *(currpage->stage); cd.more(); cd.advance())
203 5f1cf8e6 2004-05-16 devnull if (cd.current()->numcol() == 2)
204 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
205 5f1cf8e6 2004-05-16 devnull scratch.restoreall(); // in particular, floatables' goals
206 5f1cf8e6 2004-05-16 devnull int i;
207 5f1cf8e6 2004-05-16 devnull int rawht = scratch.rawht();
208 5f1cf8e6 2004-05-16 devnull int halfheight = (int)(coltol*rawht);
209 5f1cf8e6 2004-05-16 devnull // choose a goal height
210 5f1cf8e6 2004-05-16 devnull int maxht = defonly ? halfheight : htavail;
211 5f1cf8e6 2004-05-16 devnull secondtry:
212 5f1cf8e6 2004-05-16 devnull for (i = 0; i < 2; i++)
213 5f1cf8e6 2004-05-16 devnull column[i].freeall();
214 5f1cf8e6 2004-05-16 devnull leftblocked = 0;
215 5f1cf8e6 2004-05-16 devnull cd = scratch;
216 5f1cf8e6 2004-05-16 devnull while (cd.more()) {
217 5f1cf8e6 2004-05-16 devnull queue ministage; // for the minimally acceptable chunks
218 5f1cf8e6 2004-05-16 devnull ministage.freeall(); // that are to be added to either column
219 5f1cf8e6 2004-05-16 devnull while (cd.more() && !cd.current()->issentinel()) {
220 5f1cf8e6 2004-05-16 devnull ministage.enqueue(cd.current());
221 5f1cf8e6 2004-05-16 devnull cd.advance();
222 5f1cf8e6 2004-05-16 devnull }
223 5f1cf8e6 2004-05-16 devnull choosecol(&ministage, maxht);
224 5f1cf8e6 2004-05-16 devnull if (cd.more() && cd.current()->issentinel())
225 5f1cf8e6 2004-05-16 devnull cd.advance(); // past sentinel
226 5f1cf8e6 2004-05-16 devnull }
227 5f1cf8e6 2004-05-16 devnull if (height() > htavail && maxht != htavail) {
228 5f1cf8e6 2004-05-16 devnull // We tried to balance the columns, but
229 5f1cf8e6 2004-05-16 devnull // the result was too tall. Go back
230 5f1cf8e6 2004-05-16 devnull // and try again with the less ambitious
231 5f1cf8e6 2004-05-16 devnull // goal of fitting the space available.
232 5f1cf8e6 2004-05-16 devnull maxht = htavail;
233 5f1cf8e6 2004-05-16 devnull goto secondtry;
234 5f1cf8e6 2004-05-16 devnull }
235 5f1cf8e6 2004-05-16 devnull for (i = 0; i < 2; i++) {
236 5f1cf8e6 2004-05-16 devnull movefloats(&(column[i]), ((double) column[i].rawht())/currpage->pagesize);
237 5f1cf8e6 2004-05-16 devnull trimspace(&(column[i]));
238 5f1cf8e6 2004-05-16 devnull }
239 5f1cf8e6 2004-05-16 devnull if (dbg & 32) {
240 5f1cf8e6 2004-05-16 devnull printf("#multicol::compose: htavail %d maxht %d dv %d\n",
241 5f1cf8e6 2004-05-16 devnull htavail, maxht, height());
242 5f1cf8e6 2004-05-16 devnull dump();
243 5f1cf8e6 2004-05-16 devnull }
244 5f1cf8e6 2004-05-16 devnull if (defonly)
245 5f1cf8e6 2004-05-16 devnull stretch(height());
246 5f1cf8e6 2004-05-16 devnull }
247 5f1cf8e6 2004-05-16 devnull
248 5f1cf8e6 2004-05-16 devnull // A sequence of two-column ranges waits on the stage.
249 5f1cf8e6 2004-05-16 devnull // So long as the page's skeleton hasn't changed--that is, the maximum height
250 5f1cf8e6 2004-05-16 devnull // available to the two-column chunk is the same--we just use the columns that
251 5f1cf8e6 2004-05-16 devnull // have been built up so far, and choose a column into which to put the stage.
252 5f1cf8e6 2004-05-16 devnull // If the skeleton has changed, however, then we may need to make entirely
253 5f1cf8e6 2004-05-16 devnull // new decisions about which column gets what, so we recompose the whole page.
254 5f1cf8e6 2004-05-16 devnull void multicol::tryout()
255 5f1cf8e6 2004-05-16 devnull {
256 5f1cf8e6 2004-05-16 devnull if (htavail == prevhtavail)
257 5f1cf8e6 2004-05-16 devnull choosecol(currpage->stage, htavail);
258 5f1cf8e6 2004-05-16 devnull else
259 5f1cf8e6 2004-05-16 devnull currpage->compose(DRAFT);
260 5f1cf8e6 2004-05-16 devnull prevhtavail = htavail;
261 5f1cf8e6 2004-05-16 devnull }
262 5f1cf8e6 2004-05-16 devnull
263 5f1cf8e6 2004-05-16 devnull // Make both columns the same height.
264 5f1cf8e6 2004-05-16 devnull // (Maybe this should also be governed by minfull,
265 5f1cf8e6 2004-05-16 devnull // to prevent padding very underfull columns.)
266 5f1cf8e6 2004-05-16 devnull void multicol::stretch(int wantht)
267 5f1cf8e6 2004-05-16 devnull {
268 5f1cf8e6 2004-05-16 devnull if (wantht < height())
269 5f1cf8e6 2004-05-16 devnull ERROR "page %d: two-column chunk cannot shrink\n", userpn FATAL;
270 5f1cf8e6 2004-05-16 devnull for (int i = 0; i < 2; i++)
271 5f1cf8e6 2004-05-16 devnull justify(&(column[i]), wantht);
272 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
273 5f1cf8e6 2004-05-16 devnull printf("#col hts: left %d right %d\n",
274 5f1cf8e6 2004-05-16 devnull column[0].height(), column[1].height());
275 5f1cf8e6 2004-05-16 devnull }
276 5f1cf8e6 2004-05-16 devnull
277 5f1cf8e6 2004-05-16 devnull // Report an upper bound on how tall the current two-column object is.
278 5f1cf8e6 2004-05-16 devnull // The (possibly composed) heights of the two columns give a crude upper
279 5f1cf8e6 2004-05-16 devnull // bound on the total height. If the result is more than the height
280 5f1cf8e6 2004-05-16 devnull // available for the two-column object, then the columns are each
281 5f1cf8e6 2004-05-16 devnull // composed to give a better estimate of their heights.
282 5f1cf8e6 2004-05-16 devnull int multicol::height()
283 5f1cf8e6 2004-05-16 devnull {
284 5f1cf8e6 2004-05-16 devnull int retval = max(column[0].height(), column[1].height());
285 5f1cf8e6 2004-05-16 devnull if (retval < htavail)
286 5f1cf8e6 2004-05-16 devnull return retval;
287 5f1cf8e6 2004-05-16 devnull for (int i = 0; i < 2; i++) {
288 5f1cf8e6 2004-05-16 devnull movefloats(&(column[i]), ((double) column[i].height())/currpage->pagesize);
289 5f1cf8e6 2004-05-16 devnull trimspace(&(column[i]));
290 5f1cf8e6 2004-05-16 devnull }
291 5f1cf8e6 2004-05-16 devnull return max(column[0].height(), column[1].height());
292 5f1cf8e6 2004-05-16 devnull }
293 5f1cf8e6 2004-05-16 devnull
294 5f1cf8e6 2004-05-16 devnull void multicol::dump()
295 5f1cf8e6 2004-05-16 devnull {
296 5f1cf8e6 2004-05-16 devnull printf("####2COL dv %d\n", height());
297 5f1cf8e6 2004-05-16 devnull printf("# left column:\n");
298 5f1cf8e6 2004-05-16 devnull column[0].dump();
299 5f1cf8e6 2004-05-16 devnull printf("# right column:\n");
300 5f1cf8e6 2004-05-16 devnull column[1].dump();
301 5f1cf8e6 2004-05-16 devnull }
302 5f1cf8e6 2004-05-16 devnull
303 5f1cf8e6 2004-05-16 devnull // From the head of queue qp, peel off a piece whose raw height is at most space.
304 5f1cf8e6 2004-05-16 devnull int peeloff(stream *qp, int space)
305 5f1cf8e6 2004-05-16 devnull {
306 5f1cf8e6 2004-05-16 devnull stream *s1 = qp->current()->children();
307 5f1cf8e6 2004-05-16 devnull if (!(s1 && s1->more() && s1->current()->height() <= space))
308 5f1cf8e6 2004-05-16 devnull // in other words, either qp's head is
309 5f1cf8e6 2004-05-16 devnull // not nested, or its first subrange
310 5f1cf8e6 2004-05-16 devnull return 0; // is also too big, so we give up
311 5f1cf8e6 2004-05-16 devnull qp->split();
312 5f1cf8e6 2004-05-16 devnull s1 = qp->current()->children();
313 5f1cf8e6 2004-05-16 devnull stream *s2 = qp->next()->children();
314 5f1cf8e6 2004-05-16 devnull while (s2->more() && s2->current()->rawht() <= space) {
315 5f1cf8e6 2004-05-16 devnull s1->append(s2->current());
316 5f1cf8e6 2004-05-16 devnull space -= s2->current()->rawht();
317 5f1cf8e6 2004-05-16 devnull s2->advance();
318 5f1cf8e6 2004-05-16 devnull }
319 5f1cf8e6 2004-05-16 devnull return 1;
320 5f1cf8e6 2004-05-16 devnull }
321 5f1cf8e6 2004-05-16 devnull
322 5f1cf8e6 2004-05-16 devnull // There are four possibilities for consecutive calls to tryout().
323 5f1cf8e6 2004-05-16 devnull // If we're processing a sequence of single-column ranges, tryout()
324 5f1cf8e6 2004-05-16 devnull // uses the original algorithm: (1) conservative test; (2) costly test;
325 5f1cf8e6 2004-05-16 devnull // (3) split a breakable item.
326 5f1cf8e6 2004-05-16 devnull // If we're processing a sequence of double-column ranges, tryout()
327 5f1cf8e6 2004-05-16 devnull // defers to twocol->tryout(), which gradually builds up the contents
328 5f1cf8e6 2004-05-16 devnull // of the two columns until they're as tall as they can be without
329 5f1cf8e6 2004-05-16 devnull // exceeding twocol->htavail.
330 5f1cf8e6 2004-05-16 devnull // If we're processing a sequence of single-column ranges and we
331 5f1cf8e6 2004-05-16 devnull // get a double-column range, then we use compose() to build a
332 5f1cf8e6 2004-05-16 devnull // skeleton page and set twocol->htavail, the maximum height that
333 5f1cf8e6 2004-05-16 devnull // should be occupied by twocol.
334 5f1cf8e6 2004-05-16 devnull // If we're processing a sequence of double-column ranges and we
335 5f1cf8e6 2004-05-16 devnull // get a single-column range, then we should go back and squish
336 5f1cf8e6 2004-05-16 devnull // the double-column chunk as short as possible before we see if
337 5f1cf8e6 2004-05-16 devnull // we can fit the single-column range.
338 5f1cf8e6 2004-05-16 devnull void page::tryout()
339 5f1cf8e6 2004-05-16 devnull {
340 5f1cf8e6 2004-05-16 devnull if (!stage->more())
341 5f1cf8e6 2004-05-16 devnull ERROR "empty stage in page::tryout()\n" FATAL;
342 5f1cf8e6 2004-05-16 devnull int curnumcol = stage->current()->numcol();
343 5f1cf8e6 2004-05-16 devnull if (dbg & 32) {
344 5f1cf8e6 2004-05-16 devnull printf("#page::tryout(): ncol = %d, prevncol = %d; on stage:\n",
345 5f1cf8e6 2004-05-16 devnull curnumcol, prevncol);
346 5f1cf8e6 2004-05-16 devnull stage->dump();
347 5f1cf8e6 2004-05-16 devnull printf("#END of stage contents\n");
348 5f1cf8e6 2004-05-16 devnull }
349 5f1cf8e6 2004-05-16 devnull switch(curnumcol) {
350 5f1cf8e6 2004-05-16 devnull default:
351 5f1cf8e6 2004-05-16 devnull ERROR "unexpected number of columns in tryout(): %d\n",
352 5f1cf8e6 2004-05-16 devnull stage->current()->numcol() FATAL;
353 5f1cf8e6 2004-05-16 devnull break;
354 5f1cf8e6 2004-05-16 devnull case 1:
355 5f1cf8e6 2004-05-16 devnull if (prevncol == 2)
356 5f1cf8e6 2004-05-16 devnull compose(FINAL);
357 5f1cf8e6 2004-05-16 devnull if (wouldfit(stage, &definite, pagesize - twocol->height()))
358 5f1cf8e6 2004-05-16 devnull commit();
359 5f1cf8e6 2004-05-16 devnull else if (stage->current()->breakable() || blank()
360 5f1cf8e6 2004-05-16 devnull && peeloff(stage,
361 5f1cf8e6 2004-05-16 devnull pagesize - (definite.height() + twocol->height()))) {
362 5f1cf8e6 2004-05-16 devnull // first add the peeled-off part that fits
363 5f1cf8e6 2004-05-16 devnull adddef(stage->dequeue());
364 5f1cf8e6 2004-05-16 devnull // then send the rest back for later
365 5f1cf8e6 2004-05-16 devnull stage->current()->setbreaking();
366 5f1cf8e6 2004-05-16 devnull welsh();
367 5f1cf8e6 2004-05-16 devnull } else if (blank()) {
368 5f1cf8e6 2004-05-16 devnull stage->current()->rdump();
369 5f1cf8e6 2004-05-16 devnull ERROR "A %s is too big to continue.\n",
370 5f1cf8e6 2004-05-16 devnull stage->current()->typename() FATAL;
371 5f1cf8e6 2004-05-16 devnull } else
372 5f1cf8e6 2004-05-16 devnull welsh();
373 5f1cf8e6 2004-05-16 devnull break;
374 5f1cf8e6 2004-05-16 devnull case 2:
375 5f1cf8e6 2004-05-16 devnull if (prevncol == 1)
376 5f1cf8e6 2004-05-16 devnull compose(DRAFT);
377 5f1cf8e6 2004-05-16 devnull else
378 5f1cf8e6 2004-05-16 devnull twocol->tryout();
379 5f1cf8e6 2004-05-16 devnull if (scratch.height() <= pagesize)
380 5f1cf8e6 2004-05-16 devnull commit();
381 5f1cf8e6 2004-05-16 devnull else
382 5f1cf8e6 2004-05-16 devnull welsh();
383 5f1cf8e6 2004-05-16 devnull break;
384 5f1cf8e6 2004-05-16 devnull }
385 5f1cf8e6 2004-05-16 devnull prevncol = curnumcol;
386 5f1cf8e6 2004-05-16 devnull }
387 5f1cf8e6 2004-05-16 devnull
388 5f1cf8e6 2004-05-16 devnull // To compose the page, we (1) fill scratch with the stuff that's meant to
389 5f1cf8e6 2004-05-16 devnull // go on the page; (2) compose scratch as best we can; (3) set the maximum
390 5f1cf8e6 2004-05-16 devnull // height available to the two-column part of the page; (4) have the two-
391 5f1cf8e6 2004-05-16 devnull // column part compose itself.
392 5f1cf8e6 2004-05-16 devnull // In the computation of twocol->htavail, it does not matter that
393 5f1cf8e6 2004-05-16 devnull // twocol->height() is merely an upper bound, because it is merely being
394 5f1cf8e6 2004-05-16 devnull // subtracted out to give the exact total height of the single-column stuff.
395 5f1cf8e6 2004-05-16 devnull void page::compose(int final)
396 5f1cf8e6 2004-05-16 devnull {
397 5f1cf8e6 2004-05-16 devnull makescratch(final);
398 5f1cf8e6 2004-05-16 devnull int adjht = scratch.rawht();
399 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
400 5f1cf8e6 2004-05-16 devnull printf("# page %d measure %d\n", userpn, adjht);
401 5f1cf8e6 2004-05-16 devnull movefloats(&scratch, ((double) adjht)/pagesize);
402 5f1cf8e6 2004-05-16 devnull trimspace(&scratch);
403 5f1cf8e6 2004-05-16 devnull twocol->htavail = pagesize - (scratch.height() - twocol->height());
404 5f1cf8e6 2004-05-16 devnull twocol->compose(final);
405 5f1cf8e6 2004-05-16 devnull adjht = scratch.height();
406 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
407 5f1cf8e6 2004-05-16 devnull printf("# page %d measure %d after trim\n", userpn, adjht);
408 5f1cf8e6 2004-05-16 devnull }
409 5f1cf8e6 2004-05-16 devnull
410 5f1cf8e6 2004-05-16 devnull // Fill the scratch area with ranges destined for the page.
411 5f1cf8e6 2004-05-16 devnull // If defonly == 0, then add anything that's on stage--this is a trial run.
412 5f1cf8e6 2004-05-16 devnull // If defonly != 0, use only what's definitely on the page.
413 5f1cf8e6 2004-05-16 devnull void page::makescratch(int defonly)
414 5f1cf8e6 2004-05-16 devnull {
415 5f1cf8e6 2004-05-16 devnull scratch.freeall();
416 5f1cf8e6 2004-05-16 devnull stream cd;
417 5f1cf8e6 2004-05-16 devnull for (cd = definite; cd.more(); cd.advance())
418 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
419 5f1cf8e6 2004-05-16 devnull if (!defonly)
420 5f1cf8e6 2004-05-16 devnull for (cd = *stage; cd.more(); cd.advance())
421 5f1cf8e6 2004-05-16 devnull if (cd.current()->numcol() == 1)
422 5f1cf8e6 2004-05-16 devnull scratch.append(cd.current());
423 5f1cf8e6 2004-05-16 devnull if (twocol->nonempty())
424 5f1cf8e6 2004-05-16 devnull scratch.append(twocol);
425 5f1cf8e6 2004-05-16 devnull }
426 5f1cf8e6 2004-05-16 devnull
427 5f1cf8e6 2004-05-16 devnull // Accept the current contents of the stage.
428 5f1cf8e6 2004-05-16 devnull // If the stage contains two-column ranges, add a sentinel to indicate the end
429 5f1cf8e6 2004-05-16 devnull // of a chunk of stage contents.
430 5f1cf8e6 2004-05-16 devnull void page::commit()
431 5f1cf8e6 2004-05-16 devnull {
432 5f1cf8e6 2004-05-16 devnull if (dbg & 4)
433 5f1cf8e6 2004-05-16 devnull printf("#entering page::commit()\n");
434 5f1cf8e6 2004-05-16 devnull int numcol = 0;
435 5f1cf8e6 2004-05-16 devnull while (stage->more()) {
436 5f1cf8e6 2004-05-16 devnull numcol = stage->current()->numcol();
437 5f1cf8e6 2004-05-16 devnull adddef(stage->dequeue());
438 5f1cf8e6 2004-05-16 devnull }
439 5f1cf8e6 2004-05-16 devnull if (numcol == 2)
440 5f1cf8e6 2004-05-16 devnull adddef(new sentrange);
441 5f1cf8e6 2004-05-16 devnull }
442 5f1cf8e6 2004-05-16 devnull
443 5f1cf8e6 2004-05-16 devnull // Send the current contents of the stage back to its source.
444 5f1cf8e6 2004-05-16 devnull void page::welsh()
445 5f1cf8e6 2004-05-16 devnull {
446 5f1cf8e6 2004-05-16 devnull if (dbg & 4)
447 5f1cf8e6 2004-05-16 devnull printf("#entering page::welsh()\n");
448 5f1cf8e6 2004-05-16 devnull while (stage->more()) {
449 5f1cf8e6 2004-05-16 devnull range *r = stage->dequeue();
450 5f1cf8e6 2004-05-16 devnull r->enqueue(ANDBLOCK);
451 5f1cf8e6 2004-05-16 devnull }
452 5f1cf8e6 2004-05-16 devnull }
453 5f1cf8e6 2004-05-16 devnull
454 5f1cf8e6 2004-05-16 devnull enum { USonly = 1 };
455 5f1cf8e6 2004-05-16 devnull
456 5f1cf8e6 2004-05-16 devnull // So long as anything is eligible to go onto the page, keep trying.
457 5f1cf8e6 2004-05-16 devnull // Once nothing is eligible, compose and justify the page.
458 5f1cf8e6 2004-05-16 devnull void page::fill()
459 5f1cf8e6 2004-05-16 devnull {
460 5f1cf8e6 2004-05-16 devnull while (stage->prime())
461 5f1cf8e6 2004-05-16 devnull stage->pend();
462 5f1cf8e6 2004-05-16 devnull compose(FINAL);
463 5f1cf8e6 2004-05-16 devnull if (dbg & 16)
464 5f1cf8e6 2004-05-16 devnull scratch.dump();
465 5f1cf8e6 2004-05-16 devnull if (anymore()) {
466 5f1cf8e6 2004-05-16 devnull int adjht = scratch.height();
467 5f1cf8e6 2004-05-16 devnull if (adjht > minfull*pagesize) {
468 5f1cf8e6 2004-05-16 devnull justify(&scratch, pagesize);
469 5f1cf8e6 2004-05-16 devnull adjht = scratch.height();
470 5f1cf8e6 2004-05-16 devnull int stretchamt = max(pagesize - adjht, 0);
471 5f1cf8e6 2004-05-16 devnull twocol->stretch(twocol->height() + stretchamt);
472 5f1cf8e6 2004-05-16 devnull // in case the page's stretchability lies
473 5f1cf8e6 2004-05-16 devnull // entirely in its two-column part
474 5f1cf8e6 2004-05-16 devnull } else
475 5f1cf8e6 2004-05-16 devnull ERROR "page %d only %.0f%% full; will not be adjusted\n",
476 5f1cf8e6 2004-05-16 devnull userpn, 100*(double) adjht/pagesize WARNING;
477 5f1cf8e6 2004-05-16 devnull }
478 5f1cf8e6 2004-05-16 devnull }
479 5f1cf8e6 2004-05-16 devnull
480 5f1cf8e6 2004-05-16 devnull void page::adddef(range *r)
481 5f1cf8e6 2004-05-16 devnull {
482 5f1cf8e6 2004-05-16 devnull if (dbg & 4)
483 5f1cf8e6 2004-05-16 devnull printf("#entering page::adddef()\n");
484 5f1cf8e6 2004-05-16 devnull switch (r->numcol()) {
485 5f1cf8e6 2004-05-16 devnull case 1: definite.append(r);
486 5f1cf8e6 2004-05-16 devnull break;
487 5f1cf8e6 2004-05-16 devnull case 2: twocol->definite.append(r);
488 5f1cf8e6 2004-05-16 devnull break;
489 5f1cf8e6 2004-05-16 devnull default: ERROR "%d-column range unexpected\n", r->numcol() FATAL;
490 5f1cf8e6 2004-05-16 devnull }
491 5f1cf8e6 2004-05-16 devnull }
492 5f1cf8e6 2004-05-16 devnull
493 5f1cf8e6 2004-05-16 devnull int multicol::print(int cv, int col)
494 5f1cf8e6 2004-05-16 devnull {
495 5f1cf8e6 2004-05-16 devnull if (col != 0)
496 5f1cf8e6 2004-05-16 devnull ERROR "multicolumn output must start in left column\n" FATAL;
497 5f1cf8e6 2004-05-16 devnull int curv = cv, maxv = cv; // print left column
498 5f1cf8e6 2004-05-16 devnull for ( ; column[0].more(); column[0].advance()) {
499 5f1cf8e6 2004-05-16 devnull curv = column[0].current()->print(curv, 0);
500 5f1cf8e6 2004-05-16 devnull maxv = max(maxv, curv);
501 5f1cf8e6 2004-05-16 devnull }
502 5f1cf8e6 2004-05-16 devnull curv = cv; // print right column
503 5f1cf8e6 2004-05-16 devnull for ( ; column[1].more(); column[1].advance()) {
504 5f1cf8e6 2004-05-16 devnull curv = column[1].current()->print(curv, 1);
505 5f1cf8e6 2004-05-16 devnull maxv = max(maxv, curv);
506 5f1cf8e6 2004-05-16 devnull }
507 5f1cf8e6 2004-05-16 devnull return maxv;
508 5f1cf8e6 2004-05-16 devnull }
509 5f1cf8e6 2004-05-16 devnull
510 5f1cf8e6 2004-05-16 devnull void page::print()
511 5f1cf8e6 2004-05-16 devnull {
512 5f1cf8e6 2004-05-16 devnull static int tops = 1, bots = 1;
513 5f1cf8e6 2004-05-16 devnull if (!scratch.more()) {
514 5f1cf8e6 2004-05-16 devnull ERROR "## Here's what's left on squeue:\n" WARNING;
515 5f1cf8e6 2004-05-16 devnull squeue.dump();
516 5f1cf8e6 2004-05-16 devnull ERROR "## Here's what's left on bfqueue:\n" WARNING;
517 5f1cf8e6 2004-05-16 devnull bfqueue.dump();
518 5f1cf8e6 2004-05-16 devnull ERROR "## Here's what's left on ufqueue:\n" WARNING;
519 5f1cf8e6 2004-05-16 devnull ufqueue.dump();
520 5f1cf8e6 2004-05-16 devnull ERROR "page %d appears to be empty\n", userpn WARNING;
521 5f1cf8e6 2004-05-16 devnull fflush(stderr), fflush(stdout), exit(0);
522 5f1cf8e6 2004-05-16 devnull // something is very wrong if this happens
523 5f1cf8e6 2004-05-16 devnull }
524 5f1cf8e6 2004-05-16 devnull printf("p%d\n", userpn); // print troff output page number
525 5f1cf8e6 2004-05-16 devnull if (ptlist.more()) { // print page header
526 5f1cf8e6 2004-05-16 devnull ptlist.current()->print(0, 0);
527 5f1cf8e6 2004-05-16 devnull ptlist.advance();
528 5f1cf8e6 2004-05-16 devnull } else if (tops) {
529 5f1cf8e6 2004-05-16 devnull ERROR "ran out of page titles at %d\n", userpn WARNING;
530 5f1cf8e6 2004-05-16 devnull tops = 0;
531 5f1cf8e6 2004-05-16 devnull }
532 5f1cf8e6 2004-05-16 devnull int curv = 0;
533 5f1cf8e6 2004-05-16 devnull printf("V%d\n", curv = pagetop);// print page contents
534 5f1cf8e6 2004-05-16 devnull for ( ; scratch.more(); scratch.advance()) {
535 5f1cf8e6 2004-05-16 devnull curv = scratch.current()->print(curv, 0);
536 5f1cf8e6 2004-05-16 devnull }
537 5f1cf8e6 2004-05-16 devnull if (btlist.more()) { // print page footer
538 5f1cf8e6 2004-05-16 devnull btlist.current()->print(0, 0);
539 5f1cf8e6 2004-05-16 devnull btlist.advance();
540 5f1cf8e6 2004-05-16 devnull } else if (bots) {
541 5f1cf8e6 2004-05-16 devnull ERROR "ran out of page bottoms at %d\n", userpn WARNING;
542 5f1cf8e6 2004-05-16 devnull bots = 0;
543 5f1cf8e6 2004-05-16 devnull }
544 5f1cf8e6 2004-05-16 devnull printf("V%d\n", physbot); // finish troff output page
545 5f1cf8e6 2004-05-16 devnull }
546 5f1cf8e6 2004-05-16 devnull
547 5f1cf8e6 2004-05-16 devnull int pagetop = 0; // top printing margin
548 5f1cf8e6 2004-05-16 devnull int pagebot = 0; // bottom printing margin
549 5f1cf8e6 2004-05-16 devnull int physbot = 0; // physical bottom of page
550 5f1cf8e6 2004-05-16 devnull
551 5f1cf8e6 2004-05-16 devnull double minfull = 0.9; // minimum fullness before padding
552 5f1cf8e6 2004-05-16 devnull
553 5f1cf8e6 2004-05-16 devnull int pn = 0; // cardinal page number
554 5f1cf8e6 2004-05-16 devnull int userpn = 0; // page number derived from PT slugs
555 5f1cf8e6 2004-05-16 devnull
556 5f1cf8e6 2004-05-16 devnull static void makepage()
557 5f1cf8e6 2004-05-16 devnull {
558 5f1cf8e6 2004-05-16 devnull page pg(pagebot - pagetop);
559 5f1cf8e6 2004-05-16 devnull ++pn;
560 5f1cf8e6 2004-05-16 devnull userpn = ptlist.more() ? ptlist.current()->pn() : pn;
561 5f1cf8e6 2004-05-16 devnull pg.fill();
562 5f1cf8e6 2004-05-16 devnull pg.print();
563 5f1cf8e6 2004-05-16 devnull }
564 5f1cf8e6 2004-05-16 devnull
565 5f1cf8e6 2004-05-16 devnull static void conv(FILE *fp)
566 5f1cf8e6 2004-05-16 devnull {
567 5f1cf8e6 2004-05-16 devnull startup(fp); // read slugs, etc.
568 5f1cf8e6 2004-05-16 devnull while (anymore())
569 5f1cf8e6 2004-05-16 devnull makepage();
570 5f1cf8e6 2004-05-16 devnull lastrange->print(0, 0); // trailer
571 5f1cf8e6 2004-05-16 devnull checkout(); // check that everything was printed
572 5f1cf8e6 2004-05-16 devnull }
573 5f1cf8e6 2004-05-16 devnull
574 5f1cf8e6 2004-05-16 devnull int
575 5f1cf8e6 2004-05-16 devnull main(int argc, char **argv)
576 5f1cf8e6 2004-05-16 devnull {
577 5f1cf8e6 2004-05-16 devnull static FILE *fp = stdin;
578 5f1cf8e6 2004-05-16 devnull progname = argv[0];
579 5f1cf8e6 2004-05-16 devnull while (argc > 1 && argv[1][0] == '-') {
580 5f1cf8e6 2004-05-16 devnull switch (argv[1][1]) {
581 5f1cf8e6 2004-05-16 devnull case 'd':
582 5f1cf8e6 2004-05-16 devnull dbg = atoi(&argv[1][2]);
583 5f1cf8e6 2004-05-16 devnull if (dbg == 0)
584 5f1cf8e6 2004-05-16 devnull dbg = ~0;
585 5f1cf8e6 2004-05-16 devnull break;
586 5f1cf8e6 2004-05-16 devnull case 'm':
587 5f1cf8e6 2004-05-16 devnull minfull = 0.01*atof(&argv[1][2]);
588 5f1cf8e6 2004-05-16 devnull break;
589 5f1cf8e6 2004-05-16 devnull case 'c':
590 5f1cf8e6 2004-05-16 devnull coltol = 0.01*atof(&argv[1][2]);
591 5f1cf8e6 2004-05-16 devnull break;
592 5f1cf8e6 2004-05-16 devnull case 'w':
593 5f1cf8e6 2004-05-16 devnull wantwarn = 1;
594 5f1cf8e6 2004-05-16 devnull break;
595 5f1cf8e6 2004-05-16 devnull }
596 5f1cf8e6 2004-05-16 devnull argc--;
597 5f1cf8e6 2004-05-16 devnull argv++;
598 5f1cf8e6 2004-05-16 devnull }
599 5f1cf8e6 2004-05-16 devnull if (argc <= 1)
600 5f1cf8e6 2004-05-16 devnull conv(stdin);
601 5f1cf8e6 2004-05-16 devnull else
602 5f1cf8e6 2004-05-16 devnull while (--argc > 0) {
603 5f1cf8e6 2004-05-16 devnull if (strcmp(*++argv, "-") == 0)
604 5f1cf8e6 2004-05-16 devnull fp = stdin;
605 5f1cf8e6 2004-05-16 devnull else if ((fp = fopen(*argv, "r")) == NULL)
606 5f1cf8e6 2004-05-16 devnull ERROR "can't open %s\n", *argv FATAL;
607 5f1cf8e6 2004-05-16 devnull conv(fp);
608 5f1cf8e6 2004-05-16 devnull fclose(fp);
609 5f1cf8e6 2004-05-16 devnull }
610 5f1cf8e6 2004-05-16 devnull exit(0);
611 5f1cf8e6 2004-05-16 devnull return 0; /* gcc */
612 5f1cf8e6 2004-05-16 devnull }