diff options
Diffstat (limited to 'src/roff/troff/column.cpp')
-rw-r--r-- | src/roff/troff/column.cpp | 731 |
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 */ |