1 /* $NetBSD: column.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $ */
2
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22
23 #ifdef COLUMN
24
25 #include "troff.h"
26 #include "symbol.h"
27 #include "dictionary.h"
28 #include "hvunits.h"
29 #include "env.h"
30 #include "request.h"
31 #include "node.h"
32 #include "token.h"
33 #include "div.h"
34 #include "reg.h"
35 #include "stringclass.h"
36
vjustify(vunits,symbol)37 void output_file::vjustify(vunits, symbol)
38 {
39 // do nothing
40 }
41
42 struct justification_spec;
43 struct output_line;
44
45 class column : public output_file {
46 private:
47 output_file *out;
48 vunits bottom;
49 output_line *col;
50 output_line **tail;
51 void add_output_line(output_line *);
52 void begin_page(int pageno, vunits page_length);
53 void flush();
54 void print_line(hunits, vunits, node *, vunits, vunits);
55 void vjustify(vunits, symbol);
56 void transparent_char(unsigned char c);
57 void copy_file(hunits, vunits, const char *);
58 int is_printing();
59 void check_bottom();
60 public:
61 column();
62 ~column();
63 void start();
64 void output();
65 void justify(const justification_spec &);
66 void trim();
67 void reset();
68 vunits get_bottom();
69 vunits get_last_extra_space();
is_active()70 int is_active() { return out != 0; }
71 };
72
73 column *the_column = 0;
74
75 struct transparent_output_line;
76 struct vjustify_output_line;
77
78 class output_line {
79 output_line *next;
80 public:
81 output_line();
82 virtual ~output_line();
83 virtual void output(output_file *, vunits);
84 virtual transparent_output_line *as_transparent_output_line();
85 virtual vjustify_output_line *as_vjustify_output_line();
86 virtual vunits distance();
87 virtual vunits height();
88 virtual void reset();
89 virtual vunits extra_space(); // post line
90 friend class column;
91 friend class justification_spec;
92 };
93
94 class position_output_line : public output_line {
95 vunits dist;
96 public:
97 position_output_line(vunits);
98 vunits distance();
99 };
100
101 class node_output_line : public position_output_line {
102 node *nd;
103 hunits page_offset;
104 vunits before;
105 vunits after;
106 public:
107 node_output_line(vunits, node *, hunits, vunits, vunits);
108 ~node_output_line();
109 void output(output_file *, vunits);
110 vunits height();
111 vunits extra_space();
112 };
113
114 class vjustify_output_line : public position_output_line {
115 vunits current;
116 symbol typ;
117 public:
118 vjustify_output_line(vunits dist, symbol);
119 vunits height();
120 vjustify_output_line *as_vjustify_output_line();
121 void vary(vunits amount);
122 void reset();
123 symbol type();
124 };
125
type()126 inline symbol vjustify_output_line::type()
127 {
128 return typ;
129 }
130
131 class copy_file_output_line : public position_output_line {
132 symbol filename;
133 hunits hpos;
134 public:
135 copy_file_output_line(vunits, const char *, hunits);
136 void output(output_file *, vunits);
137 };
138
139 class transparent_output_line : public output_line {
140 string buf;
141 public:
142 transparent_output_line();
143 void output(output_file *, vunits);
144 void append_char(unsigned char c);
145 transparent_output_line *as_transparent_output_line();
146 };
147
output_line()148 output_line::output_line() : next(0)
149 {
150 }
151
~output_line()152 output_line::~output_line()
153 {
154 }
155
reset()156 void output_line::reset()
157 {
158 }
159
as_transparent_output_line()160 transparent_output_line *output_line::as_transparent_output_line()
161 {
162 return 0;
163 }
164
as_vjustify_output_line()165 vjustify_output_line *output_line::as_vjustify_output_line()
166 {
167 return 0;
168 }
169
output(output_file *,vunits)170 void output_line::output(output_file *, vunits)
171 {
172 }
173
distance()174 vunits output_line::distance()
175 {
176 return V0;
177 }
178
height()179 vunits output_line::height()
180 {
181 return V0;
182 }
183
extra_space()184 vunits output_line::extra_space()
185 {
186 return V0;
187 }
188
position_output_line(vunits d)189 position_output_line::position_output_line(vunits d)
190 : dist(d)
191 {
192 }
193
distance()194 vunits position_output_line::distance()
195 {
196 return dist;
197 }
198
node_output_line(vunits d,node * n,hunits po,vunits b,vunits a)199 node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
200 : position_output_line(d), nd(n), page_offset(po), before(b), after(a)
201 {
202 }
203
~node_output_line()204 node_output_line::~node_output_line()
205 {
206 delete_node_list(nd);
207 }
208
output(output_file * out,vunits pos)209 void node_output_line::output(output_file *out, vunits pos)
210 {
211 out->print_line(page_offset, pos, nd, before, after);
212 nd = 0;
213 }
214
height()215 vunits node_output_line::height()
216 {
217 return after;
218 }
219
extra_space()220 vunits node_output_line::extra_space()
221 {
222 return after;
223 }
224
vjustify_output_line(vunits d,symbol t)225 vjustify_output_line::vjustify_output_line(vunits d, symbol t)
226 : position_output_line(d), typ(t)
227 {
228 }
229
reset()230 void vjustify_output_line::reset()
231 {
232 current = V0;
233 }
234
height()235 vunits vjustify_output_line::height()
236 {
237 return current;
238 }
239
as_vjustify_output_line()240 vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
241 {
242 return this;
243 }
244
vary(vunits amount)245 inline void vjustify_output_line::vary(vunits amount)
246 {
247 current += amount;
248 }
249
transparent_output_line()250 transparent_output_line::transparent_output_line()
251 {
252 }
253
as_transparent_output_line()254 transparent_output_line *transparent_output_line::as_transparent_output_line()
255 {
256 return this;
257 }
258
append_char(unsigned char c)259 void transparent_output_line::append_char(unsigned char c)
260 {
261 assert(c != 0);
262 buf += c;
263 }
264
output(output_file * out,vunits)265 void transparent_output_line::output(output_file *out, vunits)
266 {
267 int len = buf.length();
268 for (int i = 0; i < len; i++)
269 out->transparent_char(buf[i]);
270 }
271
copy_file_output_line(vunits d,const char * f,hunits h)272 copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h)
273 : position_output_line(d), hpos(h), filename(f)
274 {
275 }
276
output(output_file * out,vunits pos)277 void copy_file_output_line::output(output_file *out, vunits pos)
278 {
279 out->copy_file(hpos, pos, filename.contents());
280 }
281
column()282 column::column()
283 : bottom(V0), col(0), tail(&col), out(0)
284 {
285 }
286
~column()287 column::~column()
288 {
289 assert(out != 0);
290 error("automatically outputting column before exiting");
291 output();
292 delete the_output;
293 }
294
start()295 void column::start()
296 {
297 assert(out == 0);
298 if (!the_output)
299 init_output();
300 assert(the_output != 0);
301 out = the_output;
302 the_output = this;
303 }
304
begin_page(int pageno,vunits page_length)305 void column::begin_page(int pageno, vunits page_length)
306 {
307 assert(out != 0);
308 if (col) {
309 error("automatically outputting column before beginning next page");
310 output();
311 the_output->begin_page(pageno, page_length);
312 }
313 else
314 out->begin_page(pageno, page_length);
315
316 }
317
flush()318 void column::flush()
319 {
320 assert(out != 0);
321 out->flush();
322 }
323
is_printing()324 int column::is_printing()
325 {
326 assert(out != 0);
327 return out->is_printing();
328 }
329
get_bottom()330 vunits column::get_bottom()
331 {
332 return bottom;
333 }
334
add_output_line(output_line * ln)335 void column::add_output_line(output_line *ln)
336 {
337 *tail = ln;
338 bottom += ln->distance();
339 bottom += ln->height();
340 ln->next = 0;
341 tail = &(*tail)->next;
342 }
343
print_line(hunits page_offset,vunits pos,node * nd,vunits before,vunits after)344 void column::print_line(hunits page_offset, vunits pos, node *nd,
345 vunits before, vunits after)
346 {
347 assert(out != 0);
348 add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
349 }
350
vjustify(vunits pos,symbol typ)351 void column::vjustify(vunits pos, symbol typ)
352 {
353 assert(out != 0);
354 add_output_line(new vjustify_output_line(pos - bottom, typ));
355 }
356
transparent_char(unsigned char c)357 void column::transparent_char(unsigned char c)
358 {
359 assert(out != 0);
360 transparent_output_line *tl = 0;
361 if (*tail)
362 tl = (*tail)->as_transparent_output_line();
363 if (!tl) {
364 tl = new transparent_output_line;
365 add_output_line(tl);
366 }
367 tl->append_char(c);
368 }
369
copy_file(hunits page_offset,vunits pos,const char * filename)370 void column::copy_file(hunits page_offset, vunits pos, const char *filename)
371 {
372 assert(out != 0);
373 add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
374 }
375
trim()376 void column::trim()
377 {
378 output_line **spp = 0;
379 for (output_line **pp = &col; *pp; pp = &(*pp)->next)
380 if ((*pp)->as_vjustify_output_line() == 0)
381 spp = 0;
382 else if (!spp)
383 spp = pp;
384 if (spp) {
385 output_line *ln = *spp;
386 *spp = 0;
387 tail = spp;
388 while (ln) {
389 output_line *tem = ln->next;
390 bottom -= ln->distance();
391 bottom -= ln->height();
392 delete ln;
393 ln = tem;
394 }
395 }
396 }
397
reset()398 void column::reset()
399 {
400 bottom = V0;
401 for (output_line *ln = col; ln; ln = ln->next) {
402 bottom += ln->distance();
403 ln->reset();
404 bottom += ln->height();
405 }
406 }
407
check_bottom()408 void column::check_bottom()
409 {
410 vunits b;
411 for (output_line *ln = col; ln; ln = ln->next) {
412 b += ln->distance();
413 b += ln->height();
414 }
415 assert(b == bottom);
416 }
417
output()418 void column::output()
419 {
420 assert(out != 0);
421 vunits vpos(V0);
422 output_line *ln = col;
423 while (ln) {
424 vpos += ln->distance();
425 ln->output(out, vpos);
426 vpos += ln->height();
427 output_line *tem = ln->next;
428 delete ln;
429 ln = tem;
430 }
431 tail = &col;
432 bottom = V0;
433 col = 0;
434 the_output = out;
435 out = 0;
436 }
437
get_last_extra_space()438 vunits column::get_last_extra_space()
439 {
440 if (!col)
441 return V0;
442 for (output_line *p = col; p->next; p = p->next)
443 ;
444 return p->extra_space();
445 }
446
447 class justification_spec {
448 vunits height;
449 symbol *type;
450 vunits *amount;
451 int n;
452 int maxn;
453 public:
454 justification_spec(vunits);
455 ~justification_spec();
456 void append(symbol t, vunits v);
457 void justify(output_line *, vunits *bottomp) const;
458 };
459
justification_spec(vunits h)460 justification_spec::justification_spec(vunits h)
461 : height(h), n(0), maxn(10)
462 {
463 type = new symbol[maxn];
464 amount = new vunits[maxn];
465 }
466
~justification_spec()467 justification_spec::~justification_spec()
468 {
469 a_delete type;
470 a_delete amount;
471 }
472
append(symbol t,vunits v)473 void justification_spec::append(symbol t, vunits v)
474 {
475 if (v <= V0) {
476 if (v < V0)
477 warning(WARN_RANGE,
478 "maximum space for vertical justification must not be negative");
479 else
480 warning(WARN_RANGE,
481 "maximum space for vertical justification must not be zero");
482 return;
483 }
484 if (n >= maxn) {
485 maxn *= 2;
486 symbol *old_type = type;
487 type = new symbol[maxn];
488 int i;
489 for (i = 0; i < n; i++)
490 type[i] = old_type[i];
491 a_delete old_type;
492 vunits *old_amount = amount;
493 amount = new vunits[maxn];
494 for (i = 0; i < n; i++)
495 amount[i] = old_amount[i];
496 a_delete old_amount;
497 }
498 assert(n < maxn);
499 type[n] = t;
500 amount[n] = v;
501 n++;
502 }
503
justify(output_line * col,vunits * bottomp) const504 void justification_spec::justify(output_line *col, vunits *bottomp) const
505 {
506 if (*bottomp >= height)
507 return;
508 vunits total;
509 output_line *p;
510 for (p = col; p; p = p->next) {
511 vjustify_output_line *sp = p->as_vjustify_output_line();
512 if (sp) {
513 symbol t = sp->type();
514 for (int i = 0; i < n; i++) {
515 if (t == type[i])
516 total += amount[i];
517 }
518 }
519 }
520 vunits gap = height - *bottomp;
521 for (p = col; p; p = p->next) {
522 vjustify_output_line *sp = p->as_vjustify_output_line();
523 if (sp) {
524 symbol t = sp->type();
525 for (int i = 0; i < n; i++) {
526 if (t == type[i]) {
527 if (total <= gap) {
528 sp->vary(amount[i]);
529 gap -= amount[i];
530 }
531 else {
532 // gap < total
533 vunits v = scale(amount[i], gap, total);
534 sp->vary(v);
535 gap -= v;
536 }
537 total -= amount[i];
538 }
539 }
540 }
541 }
542 assert(total == V0);
543 *bottomp = height - gap;
544 }
545
justify(const justification_spec & js)546 void column::justify(const justification_spec &js)
547 {
548 check_bottom();
549 js.justify(col, &bottom);
550 check_bottom();
551 }
552
column_justify()553 void column_justify()
554 {
555 vunits height;
556 if (!the_column->is_active())
557 error("can't justify column - column not active");
558 else if (get_vunits(&height, 'v')) {
559 justification_spec js(height);
560 symbol nm = get_long_name(1);
561 if (!nm.is_null()) {
562 vunits v;
563 if (get_vunits(&v, 'v')) {
564 js.append(nm, v);
565 int err = 0;
566 while (has_arg()) {
567 nm = get_long_name(1);
568 if (nm.is_null()) {
569 err = 1;
570 break;
571 }
572 if (!get_vunits(&v, 'v')) {
573 err = 1;
574 break;
575 }
576 js.append(nm, v);
577 }
578 if (!err)
579 the_column->justify(js);
580 }
581 }
582 }
583 skip_line();
584 }
585
column_start()586 void column_start()
587 {
588 if (the_column->is_active())
589 error("can't start column - column already active");
590 else
591 the_column->start();
592 skip_line();
593 }
594
column_output()595 void column_output()
596 {
597 if (!the_column->is_active())
598 error("can't output column - column not active");
599 else
600 the_column->output();
601 skip_line();
602 }
603
column_trim()604 void column_trim()
605 {
606 if (!the_column->is_active())
607 error("can't trim column - column not active");
608 else
609 the_column->trim();
610 skip_line();
611 }
612
column_reset()613 void column_reset()
614 {
615 if (!the_column->is_active())
616 error("can't reset column - column not active");
617 else
618 the_column->reset();
619 skip_line();
620 }
621
622 class column_bottom_reg : public reg {
623 public:
624 const char *get_string();
625 };
626
get_string()627 const char *column_bottom_reg::get_string()
628 {
629 return i_to_a(the_column->get_bottom().to_units());
630 }
631
632 class column_extra_space_reg : public reg {
633 public:
634 const char *get_string();
635 };
636
get_string()637 const char *column_extra_space_reg::get_string()
638 {
639 return i_to_a(the_column->get_last_extra_space().to_units());
640 }
641
642 class column_active_reg : public reg {
643 public:
644 const char *get_string();
645 };
646
get_string()647 const char *column_active_reg::get_string()
648 {
649 return the_column->is_active() ? "1" : "0";
650 }
651
652 static int no_vjustify_mode = 0;
653
654 class vjustify_node : public node {
655 symbol typ;
656 public:
657 vjustify_node(symbol);
658 int reread(int *);
659 const char *type();
660 int same(node *);
661 node *copy();
662 };
663
vjustify_node(symbol t)664 vjustify_node::vjustify_node(symbol t)
665 : typ(t)
666 {
667 }
668
copy()669 node *vjustify_node::copy()
670 {
671 return new vjustify_node(typ, div_nest_level);
672 }
673
type()674 const char *vjustify_node::type()
675 {
676 return "vjustify_node";
677 }
678
same(node * nd)679 int vjustify_node::same(node *nd)
680 {
681 return typ == ((vjustify_node *)nd)->typ;
682 }
683
reread(int * bolp)684 int vjustify_node::reread(int *bolp)
685 {
686 curdiv->vjustify(typ);
687 *bolp = 1;
688 return 1;
689 }
690
vjustify(symbol type)691 void macro_diversion::vjustify(symbol type)
692 {
693 if (!no_vjustify_mode)
694 mac->append(new vjustify_node(type));
695 }
696
vjustify(symbol type)697 void top_level_diversion::vjustify(symbol type)
698 {
699 if (no_space_mode || no_vjustify_mode)
700 return;
701 assert(first_page_begun); // I'm not sure about this.
702 the_output->vjustify(vertical_position, type);
703 }
704
no_vjustify()705 void no_vjustify()
706 {
707 skip_line();
708 no_vjustify_mode = 1;
709 }
710
restore_vjustify()711 void restore_vjustify()
712 {
713 skip_line();
714 no_vjustify_mode = 0;
715 }
716
init_column_requests()717 void init_column_requests()
718 {
719 the_column = new column;
720 init_request("cols", column_start);
721 init_request("colo", column_output);
722 init_request("colj", column_justify);
723 init_request("colr", column_reset);
724 init_request("colt", column_trim);
725 init_request("nvj", no_vjustify);
726 init_request("rvj", restore_vjustify);
727 number_reg_dictionary.define(".colb", new column_bottom_reg);
728 number_reg_dictionary.define(".colx", new column_extra_space_reg);
729 number_reg_dictionary.define(".cola", new column_active_reg);
730 number_reg_dictionary.define(".nvj",
731 new constant_int_reg(&no_vjustify_mode));
732 }
733
734 #endif /* COLUMN */
735