summaryrefslogtreecommitdiffstats
path: root/src/roff/troff/column.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/roff/troff/column.cpp')
-rw-r--r--src/roff/troff/column.cpp731
1 files changed, 731 insertions, 0 deletions
diff --git a/src/roff/troff/column.cpp b/src/roff/troff/column.cpp
new file mode 100644
index 0000000..55563ba
--- /dev/null
+++ b/src/roff/troff/column.cpp
@@ -0,0 +1,731 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff 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 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+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, see <http://www.gnu.org/licenses/>. */
+
+#ifdef COLUMN
+
+#include "troff.h"
+#include "symbol.h"
+#include "dictionary.h"
+#include "hvunits.h"
+#include "env.h"
+#include "request.h"
+#include "node.h"
+#include "token.h"
+#include "div.h"
+#include "reg.h"
+#include "stringclass.h"
+
+void output_file::vjustify(vunits, symbol)
+{
+ // do nothing
+}
+
+struct justification_spec;
+struct output_line;
+
+class column : public output_file {
+private:
+ output_file *out;
+ vunits bottom;
+ output_line *col;
+ output_line **tail;
+ void add_output_line(output_line *);
+ void begin_page(int pageno, vunits page_length);
+ void flush();
+ void print_line(hunits, vunits, node *, vunits, vunits);
+ void vjustify(vunits, symbol);
+ void transparent_char(unsigned char c);
+ void copy_file(hunits, vunits, const char *);
+ int is_printing();
+ void check_bottom();
+public:
+ column();
+ ~column();
+ void start();
+ void output();
+ void justify(const justification_spec &);
+ void trim();
+ void reset();
+ vunits get_bottom();
+ vunits get_last_extra_space();
+ int is_active() { return out != 0; }
+};
+
+column *the_column = 0;
+
+struct transparent_output_line;
+struct vjustify_output_line;
+
+class output_line {
+ output_line *next;
+public:
+ output_line();
+ virtual ~output_line();
+ virtual void output(output_file *, vunits);
+ virtual transparent_output_line *as_transparent_output_line();
+ virtual vjustify_output_line *as_vjustify_output_line();
+ virtual vunits distance();
+ virtual vunits height();
+ virtual void reset();
+ virtual vunits extra_space(); // post line
+ friend class column;
+ friend class justification_spec;
+};
+
+class position_output_line : public output_line {
+ vunits dist;
+public:
+ position_output_line(vunits);
+ vunits distance();
+};
+
+class node_output_line : public position_output_line {
+ node *nd;
+ hunits page_offset;
+ vunits before;
+ vunits after;
+public:
+ node_output_line(vunits, node *, hunits, vunits, vunits);
+ ~node_output_line();
+ void output(output_file *, vunits);
+ vunits height();
+ vunits extra_space();
+};
+
+class vjustify_output_line : public position_output_line {
+ vunits current;
+ symbol typ;
+public:
+ vjustify_output_line(vunits dist, symbol);
+ vunits height();
+ vjustify_output_line *as_vjustify_output_line();
+ void vary(vunits amount);
+ void reset();
+ symbol type();
+};
+
+inline symbol vjustify_output_line::type()
+{
+ return typ;
+}
+
+class copy_file_output_line : public position_output_line {
+ symbol filename;
+ hunits hpos;
+public:
+ copy_file_output_line(vunits, const char *, hunits);
+ void output(output_file *, vunits);
+};
+
+class transparent_output_line : public output_line {
+ string buf;
+public:
+ transparent_output_line();
+ void output(output_file *, vunits);
+ void append_char(unsigned char c);
+ transparent_output_line *as_transparent_output_line();
+};
+
+output_line::output_line() : next(0)
+{
+}
+
+output_line::~output_line()
+{
+}
+
+void output_line::reset()
+{
+}
+
+transparent_output_line *output_line::as_transparent_output_line()
+{
+ return 0;
+}
+
+vjustify_output_line *output_line::as_vjustify_output_line()
+{
+ return 0;
+}
+
+void output_line::output(output_file *, vunits)
+{
+}
+
+vunits output_line::distance()
+{
+ return V0;
+}
+
+vunits output_line::height()
+{
+ return V0;
+}
+
+vunits output_line::extra_space()
+{
+ return V0;
+}
+
+position_output_line::position_output_line(vunits d)
+: dist(d)
+{
+}
+
+vunits position_output_line::distance()
+{
+ return dist;
+}
+
+node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
+: position_output_line(d), nd(n), page_offset(po), before(b), after(a)
+{
+}
+
+node_output_line::~node_output_line()
+{
+ delete_node_list(nd);
+}
+
+void node_output_line::output(output_file *out, vunits pos)
+{
+ out->print_line(page_offset, pos, nd, before, after);
+ nd = 0;
+}
+
+vunits node_output_line::height()
+{
+ return after;
+}
+
+vunits node_output_line::extra_space()
+{
+ return after;
+}
+
+vjustify_output_line::vjustify_output_line(vunits d, symbol t)
+: position_output_line(d), typ(t)
+{
+}
+
+void vjustify_output_line::reset()
+{
+ current = V0;
+}
+
+vunits vjustify_output_line::height()
+{
+ return current;
+}
+
+vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
+{
+ return this;
+}
+
+inline void vjustify_output_line::vary(vunits amount)
+{
+ current += amount;
+}
+
+transparent_output_line::transparent_output_line()
+{
+}
+
+transparent_output_line *transparent_output_line::as_transparent_output_line()
+{
+ return this;
+}
+
+void transparent_output_line::append_char(unsigned char c)
+{
+ assert(c != 0);
+ buf += c;
+}
+
+void transparent_output_line::output(output_file *out, vunits)
+{
+ int len = buf.length();
+ for (int i = 0; i < len; i++)
+ out->transparent_char(buf[i]);
+}
+
+copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h)
+: position_output_line(d), hpos(h), filename(f)
+{
+}
+
+void copy_file_output_line::output(output_file *out, vunits pos)
+{
+ out->copy_file(hpos, pos, filename.contents());
+}
+
+column::column()
+: bottom(V0), col(0), tail(&col), out(0)
+{
+}
+
+column::~column()
+{
+ assert(out != 0);
+ error("automatically outputting column before exiting");
+ output();
+ delete the_output;
+}
+
+void column::start()
+{
+ assert(out == 0);
+ if (!the_output)
+ init_output();
+ assert(the_output != 0);
+ out = the_output;
+ the_output = this;
+}
+
+void column::begin_page(int pageno, vunits page_length)
+{
+ assert(out != 0);
+ if (col) {
+ error("automatically outputting column before beginning next page");
+ output();
+ the_output->begin_page(pageno, page_length);
+ }
+ else
+ out->begin_page(pageno, page_length);
+
+}
+
+void column::flush()
+{
+ assert(out != 0);
+ out->flush();
+}
+
+int column::is_printing()
+{
+ assert(out != 0);
+ return out->is_printing();
+}
+
+vunits column::get_bottom()
+{
+ return bottom;
+}
+
+void column::add_output_line(output_line *ln)
+{
+ *tail = ln;
+ bottom += ln->distance();
+ bottom += ln->height();
+ ln->next = 0;
+ tail = &(*tail)->next;
+}
+
+void column::print_line(hunits page_offset, vunits pos, node *nd,
+ vunits before, vunits after)
+{
+ assert(out != 0);
+ add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
+}
+
+void column::vjustify(vunits pos, symbol typ)
+{
+ assert(out != 0);
+ add_output_line(new vjustify_output_line(pos - bottom, typ));
+}
+
+void column::transparent_char(unsigned char c)
+{
+ assert(out != 0);
+ transparent_output_line *tl = 0;
+ if (*tail)
+ tl = (*tail)->as_transparent_output_line();
+ if (!tl) {
+ tl = new transparent_output_line;
+ add_output_line(tl);
+ }
+ tl->append_char(c);
+}
+
+void column::copy_file(hunits page_offset, vunits pos, const char *filename)
+{
+ assert(out != 0);
+ add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
+}
+
+void column::trim()
+{
+ output_line **spp = 0;
+ for (output_line **pp = &col; *pp; pp = &(*pp)->next)
+ if ((*pp)->as_vjustify_output_line() == 0)
+ spp = 0;
+ else if (!spp)
+ spp = pp;
+ if (spp) {
+ output_line *ln = *spp;
+ *spp = 0;
+ tail = spp;
+ while (ln) {
+ output_line *tem = ln->next;
+ bottom -= ln->distance();
+ bottom -= ln->height();
+ delete ln;
+ ln = tem;
+ }
+ }
+}
+
+void column::reset()
+{
+ bottom = V0;
+ for (output_line *ln = col; ln; ln = ln->next) {
+ bottom += ln->distance();
+ ln->reset();
+ bottom += ln->height();
+ }
+}
+
+void column::check_bottom()
+{
+ vunits b;
+ for (output_line *ln = col; ln; ln = ln->next) {
+ b += ln->distance();
+ b += ln->height();
+ }
+ assert(b == bottom);
+}
+
+void column::output()
+{
+ assert(out != 0);
+ vunits vpos(V0);
+ output_line *ln = col;
+ while (ln) {
+ vpos += ln->distance();
+ ln->output(out, vpos);
+ vpos += ln->height();
+ output_line *tem = ln->next;
+ delete ln;
+ ln = tem;
+ }
+ tail = &col;
+ bottom = V0;
+ col = 0;
+ the_output = out;
+ out = 0;
+}
+
+vunits column::get_last_extra_space()
+{
+ if (!col)
+ return V0;
+ for (output_line *p = col; p->next; p = p->next)
+ ;
+ return p->extra_space();
+}
+
+class justification_spec {
+ vunits height;
+ symbol *type;
+ vunits *amount;
+ int n;
+ int maxn;
+public:
+ justification_spec(vunits);
+ ~justification_spec();
+ void append(symbol t, vunits v);
+ void justify(output_line *, vunits *bottomp) const;
+};
+
+justification_spec::justification_spec(vunits h)
+: height(h), n(0), maxn(10)
+{
+ type = new symbol[maxn];
+ amount = new vunits[maxn];
+}
+
+justification_spec::~justification_spec()
+{
+ delete[] type;
+ delete[] amount;
+}
+
+void justification_spec::append(symbol t, vunits v)
+{
+ if (v <= V0) {
+ if (v < V0)
+ warning(WARN_RANGE,
+ "maximum space for vertical justification must not be negative");
+ else
+ warning(WARN_RANGE,
+ "maximum space for vertical justification must not be zero");
+ return;
+ }
+ if (n >= maxn) {
+ maxn *= 2;
+ symbol *old_type = type;
+ type = new symbol[maxn];
+ int i;
+ for (i = 0; i < n; i++)
+ type[i] = old_type[i];
+ delete[] old_type;
+ vunits *old_amount = amount;
+ amount = new vunits[maxn];
+ for (i = 0; i < n; i++)
+ amount[i] = old_amount[i];
+ delete[] old_amount;
+ }
+ assert(n < maxn);
+ type[n] = t;
+ amount[n] = v;
+ n++;
+}
+
+void justification_spec::justify(output_line *col, vunits *bottomp) const
+{
+ if (*bottomp >= height)
+ return;
+ vunits total;
+ output_line *p;
+ for (p = col; p; p = p->next) {
+ vjustify_output_line *sp = p->as_vjustify_output_line();
+ if (sp) {
+ symbol t = sp->type();
+ for (int i = 0; i < n; i++) {
+ if (t == type[i])
+ total += amount[i];
+ }
+ }
+ }
+ vunits gap = height - *bottomp;
+ for (p = col; p; p = p->next) {
+ vjustify_output_line *sp = p->as_vjustify_output_line();
+ if (sp) {
+ symbol t = sp->type();
+ for (int i = 0; i < n; i++) {
+ if (t == type[i]) {
+ if (total <= gap) {
+ sp->vary(amount[i]);
+ gap -= amount[i];
+ }
+ else {
+ // gap < total
+ vunits v = scale(amount[i], gap, total);
+ sp->vary(v);
+ gap -= v;
+ }
+ total -= amount[i];
+ }
+ }
+ }
+ }
+ assert(total == V0);
+ *bottomp = height - gap;
+}
+
+void column::justify(const justification_spec &js)
+{
+ check_bottom();
+ js.justify(col, &bottom);
+ check_bottom();
+}
+
+void column_justify()
+{
+ vunits height;
+ if (!the_column->is_active())
+ error("can't justify column - column not active");
+ else if (get_vunits(&height, 'v')) {
+ justification_spec js(height);
+ symbol nm = get_long_name(true /* required */);
+ if (!nm.is_null()) {
+ vunits v;
+ if (get_vunits(&v, 'v')) {
+ js.append(nm, v);
+ int err = 0;
+ while (has_arg()) {
+ nm = get_long_name(true /* required */);
+ if (nm.is_null()) {
+ err = 1;
+ break;
+ }
+ if (!get_vunits(&v, 'v')) {
+ err = 1;
+ break;
+ }
+ js.append(nm, v);
+ }
+ if (!err)
+ the_column->justify(js);
+ }
+ }
+ }
+ skip_line();
+}
+
+void column_start()
+{
+ if (the_column->is_active())
+ error("can't start column - column already active");
+ else
+ the_column->start();
+ skip_line();
+}
+
+void column_output()
+{
+ if (!the_column->is_active())
+ error("can't output column - column not active");
+ else
+ the_column->output();
+ skip_line();
+}
+
+void column_trim()
+{
+ if (!the_column->is_active())
+ error("can't trim column - column not active");
+ else
+ the_column->trim();
+ skip_line();
+}
+
+void column_reset()
+{
+ if (!the_column->is_active())
+ error("can't reset column - column not active");
+ else
+ the_column->reset();
+ skip_line();
+}
+
+class column_bottom_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *column_bottom_reg::get_string()
+{
+ return i_to_a(the_column->get_bottom().to_units());
+}
+
+class column_extra_space_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *column_extra_space_reg::get_string()
+{
+ return i_to_a(the_column->get_last_extra_space().to_units());
+}
+
+class column_active_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *column_active_reg::get_string()
+{
+ return the_column->is_active() ? "1" : "0";
+}
+
+static int no_vjustify_mode = 0;
+
+class vjustify_node : public node {
+ symbol typ;
+public:
+ vjustify_node(symbol);
+ int reread(int *);
+ const char *type();
+ int same(node *);
+ node *copy();
+};
+
+vjustify_node::vjustify_node(symbol t)
+: typ(t)
+{
+}
+
+node *vjustify_node::copy()
+{
+ return new vjustify_node(typ, div_nest_level);
+}
+
+const char *vjustify_node::type()
+{
+ return "vjustify_node";
+}
+
+int vjustify_node::same(node *nd)
+{
+ return typ == ((vjustify_node *)nd)->typ;
+}
+
+int vjustify_node::reread(int *bolp)
+{
+ curdiv->vjustify(typ);
+ *bolp = 1;
+ return 1;
+}
+
+void macro_diversion::vjustify(symbol type)
+{
+ if (!no_vjustify_mode)
+ mac->append(new vjustify_node(type));
+}
+
+void top_level_diversion::vjustify(symbol type)
+{
+ if (no_space_mode || no_vjustify_mode)
+ return;
+ assert(first_page_begun); // I'm not sure about this.
+ the_output->vjustify(vertical_position, type);
+}
+
+void no_vjustify()
+{
+ skip_line();
+ no_vjustify_mode = 1;
+}
+
+void restore_vjustify()
+{
+ skip_line();
+ no_vjustify_mode = 0;
+}
+
+void init_column_requests()
+{
+ the_column = new column;
+ init_request("cols", column_start);
+ init_request("colo", column_output);
+ init_request("colj", column_justify);
+ init_request("colr", column_reset);
+ init_request("colt", column_trim);
+ init_request("nvj", no_vjustify);
+ init_request("rvj", restore_vjustify);
+ register_dictionary.define(".colb", new column_bottom_reg);
+ register_dictionary.define(".colx", new column_extra_space_reg);
+ register_dictionary.define(".cola", new column_active_reg);
+ register_dictionary.define(".nvj",
+ new readonly_register(&no_vjustify_mode));
+}
+
+#endif /* COLUMN */