Copyright 2017
[s-roff.git] / src / troff / column.cpp
blob6f8a60627ed06fb41f2350172007df23a8d00981
1 /*@
2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
10 * version.
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
21 #ifdef COLUMN
23 #include "config.h"
24 #include "troff-config.h"
26 #include "stringclass.h"
27 #include "symbol.h"
29 #include "div.h"
30 #include "dictionary.h"
31 #include "env.h"
32 #include "hvunits.h"
33 #include "node.h"
34 #include "reg.h"
35 #include "request.h"
36 #include "token.h"
37 #include "troff.h"
39 void output_file::vjustify(vunits, symbol)
41 // do nothing
44 struct justification_spec;
45 struct output_line;
47 class column
48 : public output_file
50 output_file *out;
51 vunits bottom;
52 output_line *col;
53 output_line **tail;
55 void add_output_line(output_line *);
56 void begin_page(int pageno, vunits page_length);
57 void flush();
58 void print_line(hunits, vunits, node *, vunits, vunits);
59 void vjustify(vunits, symbol);
60 void transparent_char(unsigned char c);
61 void copy_file(hunits, vunits, const char *);
62 int is_printing();
63 void check_bottom();
65 public:
66 column();
67 ~column();
68 void start();
69 void output();
70 void justify(const justification_spec &);
71 void trim();
72 void reset();
73 vunits get_bottom();
74 vunits get_last_extra_space();
75 int is_active() { return out != 0; }
78 column *the_column = 0;
80 struct transparent_output_line;
81 struct vjustify_output_line;
83 class output_line
85 friend class column;
86 friend class justification_spec;
88 output_line *next;
90 public:
91 output_line();
92 virtual ~output_line();
93 virtual void output(output_file *, vunits);
94 virtual transparent_output_line *as_transparent_output_line();
95 virtual vjustify_output_line *as_vjustify_output_line();
96 virtual vunits distance();
97 virtual vunits height();
98 virtual void reset();
99 virtual vunits extra_space(); // post line
102 class position_output_line
103 : public output_line
105 vunits dist;
107 public:
108 position_output_line(vunits);
109 vunits distance();
112 class node_output_line
113 : public position_output_line
115 node *nd;
116 hunits page_offset;
117 vunits before;
118 vunits after;
120 public:
121 node_output_line(vunits, node *, hunits, vunits, vunits);
122 ~node_output_line();
123 void output(output_file *, vunits);
124 vunits height();
125 vunits extra_space();
128 class vjustify_output_line
129 : public position_output_line
131 vunits current;
132 symbol typ;
134 public:
135 vjustify_output_line(vunits dist, symbol);
136 vunits height();
137 vjustify_output_line *as_vjustify_output_line();
138 void vary(vunits amount);
139 void reset();
140 symbol type();
143 inline symbol vjustify_output_line::type()
145 return typ;
148 class copy_file_output_line
149 : public position_output_line
151 symbol filename;
152 hunits hpos;
154 public:
155 copy_file_output_line(vunits, const char *, hunits);
156 void output(output_file *, vunits);
159 class transparent_output_line
160 : public output_line
162 string buf;
164 public:
165 transparent_output_line();
166 void output(output_file *, vunits);
167 void append_char(unsigned char c);
168 transparent_output_line *as_transparent_output_line();
171 output_line::output_line() : next(0)
175 output_line::~output_line()
179 void output_line::reset()
183 transparent_output_line *output_line::as_transparent_output_line()
185 return 0;
188 vjustify_output_line *output_line::as_vjustify_output_line()
190 return 0;
193 void output_line::output(output_file *, vunits)
197 vunits output_line::distance()
199 return V0;
202 vunits output_line::height()
204 return V0;
207 vunits output_line::extra_space()
209 return V0;
212 position_output_line::position_output_line(vunits d)
213 : dist(d)
217 vunits position_output_line::distance()
219 return dist;
222 node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
223 : position_output_line(d), nd(n), page_offset(po), before(b), after(a)
227 node_output_line::~node_output_line()
229 delete_node_list(nd);
232 void node_output_line::output(output_file *out, vunits pos)
234 out->print_line(page_offset, pos, nd, before, after);
235 nd = 0;
238 vunits node_output_line::height()
240 return after;
243 vunits node_output_line::extra_space()
245 return after;
248 vjustify_output_line::vjustify_output_line(vunits d, symbol t)
249 : position_output_line(d), typ(t)
253 void vjustify_output_line::reset()
255 current = V0;
258 vunits vjustify_output_line::height()
260 return current;
263 vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
265 return this;
268 inline void vjustify_output_line::vary(vunits amount)
270 current += amount;
273 transparent_output_line::transparent_output_line()
277 transparent_output_line *transparent_output_line::as_transparent_output_line()
279 return this;
282 void transparent_output_line::append_char(unsigned char c)
284 assert(c != 0);
285 buf += c;
288 void transparent_output_line::output(output_file *out, vunits)
290 int len = buf.length();
291 for (int i = 0; i < len; i++)
292 out->transparent_char(buf[i]);
295 copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h)
296 : position_output_line(d), hpos(h), filename(f)
300 void copy_file_output_line::output(output_file *out, vunits pos)
302 out->copy_file(hpos, pos, filename.contents());
305 column::column()
306 : bottom(V0), col(0), tail(&col), out(0)
310 column::~column()
312 assert(out != 0);
313 error("automatically outputting column before exiting");
314 output();
315 delete the_output;
318 void column::start()
320 assert(out == 0);
321 if (!the_output)
322 init_output();
323 assert(the_output != 0);
324 out = the_output;
325 the_output = this;
328 void column::begin_page(int pageno, vunits page_length)
330 assert(out != 0);
331 if (col) {
332 error("automatically outputting column before beginning next page");
333 output();
334 the_output->begin_page(pageno, page_length);
336 else
337 out->begin_page(pageno, page_length);
341 void column::flush()
343 assert(out != 0);
344 out->flush();
347 int column::is_printing()
349 assert(out != 0);
350 return out->is_printing();
353 vunits column::get_bottom()
355 return bottom;
358 void column::add_output_line(output_line *ln)
360 *tail = ln;
361 bottom += ln->distance();
362 bottom += ln->height();
363 ln->next = 0;
364 tail = &(*tail)->next;
367 void column::print_line(hunits page_offset, vunits pos, node *nd,
368 vunits before, vunits after)
370 assert(out != 0);
371 add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
374 void column::vjustify(vunits pos, symbol typ)
376 assert(out != 0);
377 add_output_line(new vjustify_output_line(pos - bottom, typ));
380 void column::transparent_char(unsigned char c)
382 assert(out != 0);
383 transparent_output_line *tl = 0;
384 if (*tail)
385 tl = (*tail)->as_transparent_output_line();
386 if (!tl) {
387 tl = new transparent_output_line;
388 add_output_line(tl);
390 tl->append_char(c);
393 void column::copy_file(hunits page_offset, vunits pos, const char *filename)
395 assert(out != 0);
396 add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
399 void column::trim()
401 output_line **spp = 0;
402 for (output_line **pp = &col; *pp; pp = &(*pp)->next)
403 if ((*pp)->as_vjustify_output_line() == 0)
404 spp = 0;
405 else if (!spp)
406 spp = pp;
407 if (spp) {
408 output_line *ln = *spp;
409 *spp = 0;
410 tail = spp;
411 while (ln) {
412 output_line *tem = ln->next;
413 bottom -= ln->distance();
414 bottom -= ln->height();
415 delete ln;
416 ln = tem;
421 void column::reset()
423 bottom = V0;
424 for (output_line *ln = col; ln; ln = ln->next) {
425 bottom += ln->distance();
426 ln->reset();
427 bottom += ln->height();
431 void column::check_bottom()
433 vunits b;
434 for (output_line *ln = col; ln; ln = ln->next) {
435 b += ln->distance();
436 b += ln->height();
438 assert(b == bottom);
441 void column::output()
443 assert(out != 0);
444 vunits vpos(V0);
445 output_line *ln = col;
446 while (ln) {
447 vpos += ln->distance();
448 ln->output(out, vpos);
449 vpos += ln->height();
450 output_line *tem = ln->next;
451 delete ln;
452 ln = tem;
454 tail = &col;
455 bottom = V0;
456 col = 0;
457 the_output = out;
458 out = 0;
461 vunits column::get_last_extra_space()
463 if (!col)
464 return V0;
465 for (output_line *p = col; p->next; p = p->next)
467 return p->extra_space();
470 class justification_spec
472 vunits height;
473 symbol *type;
474 vunits *amount;
475 int n;
476 int maxn;
478 public:
479 justification_spec(vunits);
480 ~justification_spec();
481 void append(symbol t, vunits v);
482 void justify(output_line *, vunits *bottomp) const;
485 justification_spec::justification_spec(vunits h)
486 : height(h), n(0), maxn(10)
488 type = new symbol[maxn];
489 amount = new vunits[maxn];
492 justification_spec::~justification_spec()
494 a_delete type;
495 a_delete amount;
498 void justification_spec::append(symbol t, vunits v)
500 if (v <= V0) {
501 if (v < V0)
502 warning(WARN_RANGE,
503 "maximum space for vertical justification must not be negative");
504 else
505 warning(WARN_RANGE,
506 "maximum space for vertical justification must not be zero");
507 return;
509 if (n >= maxn) {
510 maxn *= 2;
511 symbol *old_type = type;
512 type = new symbol[maxn];
513 int i;
514 for (i = 0; i < n; i++)
515 type[i] = old_type[i];
516 a_delete old_type;
517 vunits *old_amount = amount;
518 amount = new vunits[maxn];
519 for (i = 0; i < n; i++)
520 amount[i] = old_amount[i];
521 a_delete old_amount;
523 assert(n < maxn);
524 type[n] = t;
525 amount[n] = v;
526 n++;
529 void justification_spec::justify(output_line *col, vunits *bottomp) const
531 if (*bottomp >= height)
532 return;
533 vunits total;
534 output_line *p;
535 for (p = col; p; p = p->next) {
536 vjustify_output_line *sp = p->as_vjustify_output_line();
537 if (sp) {
538 symbol t = sp->type();
539 for (int i = 0; i < n; i++) {
540 if (t == type[i])
541 total += amount[i];
545 vunits gap = height - *bottomp;
546 for (p = col; p; p = p->next) {
547 vjustify_output_line *sp = p->as_vjustify_output_line();
548 if (sp) {
549 symbol t = sp->type();
550 for (int i = 0; i < n; i++) {
551 if (t == type[i]) {
552 if (total <= gap) {
553 sp->vary(amount[i]);
554 gap -= amount[i];
556 else {
557 // gap < total
558 vunits v = scale(amount[i], gap, total);
559 sp->vary(v);
560 gap -= v;
562 total -= amount[i];
567 assert(total == V0);
568 *bottomp = height - gap;
571 void column::justify(const justification_spec &js)
573 check_bottom();
574 js.justify(col, &bottom);
575 check_bottom();
578 void column_justify()
580 vunits height;
581 if (!the_column->is_active())
582 error("can't justify column - column not active");
583 else if (get_vunits(&height, 'v')) {
584 justification_spec js(height);
585 symbol nm = get_long_name(1);
586 if (!nm.is_null()) {
587 vunits v;
588 if (get_vunits(&v, 'v')) {
589 js.append(nm, v);
590 int err = 0;
591 while (has_arg()) {
592 nm = get_long_name(1);
593 if (nm.is_null()) {
594 err = 1;
595 break;
597 if (!get_vunits(&v, 'v')) {
598 err = 1;
599 break;
601 js.append(nm, v);
603 if (!err)
604 the_column->justify(js);
608 skip_line();
611 void column_start()
613 if (the_column->is_active())
614 error("can't start column - column already active");
615 else
616 the_column->start();
617 skip_line();
620 void column_output()
622 if (!the_column->is_active())
623 error("can't output column - column not active");
624 else
625 the_column->output();
626 skip_line();
629 void column_trim()
631 if (!the_column->is_active())
632 error("can't trim column - column not active");
633 else
634 the_column->trim();
635 skip_line();
638 void column_reset()
640 if (!the_column->is_active())
641 error("can't reset column - column not active");
642 else
643 the_column->reset();
644 skip_line();
647 class column_bottom_reg
648 : public reg
650 public:
651 const char *get_string();
654 const char *column_bottom_reg::get_string()
656 return i_to_a(the_column->get_bottom().to_units());
659 class column_extra_space_reg
660 : public reg
662 public:
663 const char *get_string();
666 const char *column_extra_space_reg::get_string()
668 return i_to_a(the_column->get_last_extra_space().to_units());
671 class column_active_reg
672 : public reg
674 public:
675 const char *get_string();
678 const char *column_active_reg::get_string()
680 return the_column->is_active() ? "1" : "0";
683 static int no_vjustify_mode = 0;
685 class vjustify_node
686 : public node
688 symbol typ;
690 public:
691 vjustify_node(symbol);
692 int reread(int *);
693 const char *type();
694 int same(node *);
695 node *copy();
698 vjustify_node::vjustify_node(symbol t)
699 : typ(t)
703 node *vjustify_node::copy()
705 return new vjustify_node(typ, div_nest_level);
708 const char *vjustify_node::type()
710 return "vjustify_node";
713 int vjustify_node::same(node *nd)
715 return typ == ((vjustify_node *)nd)->typ;
718 int vjustify_node::reread(int *bolp)
720 curdiv->vjustify(typ);
721 *bolp = 1;
722 return 1;
725 void macro_diversion::vjustify(symbol type)
727 if (!no_vjustify_mode)
728 mac->append(new vjustify_node(type));
731 void top_level_diversion::vjustify(symbol type)
733 if (no_space_mode || no_vjustify_mode)
734 return;
735 assert(first_page_begun); // I'm not sure about this.
736 the_output->vjustify(vertical_position, type);
739 void no_vjustify()
741 skip_line();
742 no_vjustify_mode = 1;
745 void restore_vjustify()
747 skip_line();
748 no_vjustify_mode = 0;
751 void init_column_requests()
753 the_column = new column;
754 init_request("cols", column_start);
755 init_request("colo", column_output);
756 init_request("colj", column_justify);
757 init_request("colr", column_reset);
758 init_request("colt", column_trim);
759 init_request("nvj", no_vjustify);
760 init_request("rvj", restore_vjustify);
761 number_reg_dictionary.define(".colb", new column_bottom_reg);
762 number_reg_dictionary.define(".colx", new column_extra_space_reg);
763 number_reg_dictionary.define(".cola", new column_active_reg);
764 number_reg_dictionary.define(".nvj",
765 new constant_int_reg(&no_vjustify_mode));
768 #endif /* COLUMN */
769 // s-it2-mode