diff options
author | ru <ru@FreeBSD.org> | 2001-04-17 12:12:05 +0000 |
---|---|---|
committer | ru <ru@FreeBSD.org> | 2001-04-17 12:12:05 +0000 |
commit | 0e0a0e6429c7113acf15c4c94bd5fe94c45f9e99 (patch) | |
tree | f3137c4283de8869ebcae1dd0fe43f590276c1dc /contrib/groff/src/devices | |
parent | a812d8b090bc4edc23150bff257717b24f282e41 (diff) | |
download | FreeBSD-src-0e0a0e6429c7113acf15c4c94bd5fe94c45f9e99.zip FreeBSD-src-0e0a0e6429c7113acf15c4c94bd5fe94c45f9e99.tar.gz |
Virgin import of FSF groff v1.17
Diffstat (limited to 'contrib/groff/src/devices')
30 files changed, 12484 insertions, 6 deletions
diff --git a/contrib/groff/src/devices/grodvi/Makefile.sub b/contrib/groff/src/devices/grodvi/Makefile.sub new file mode 100644 index 0000000..0e5d32c --- /dev/null +++ b/contrib/groff/src/devices/grodvi/Makefile.sub @@ -0,0 +1,6 @@ +PROG=grodvi +MAN1=grodvi.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=dvi.o +CCSRCS=$(srcdir)/dvi.cc diff --git a/contrib/groff/src/devices/grodvi/dvi.cc b/contrib/groff/src/devices/grodvi/dvi.cc new file mode 100644 index 0000000..74422f8 --- /dev/null +++ b/contrib/groff/src/devices/grodvi/dvi.cc @@ -0,0 +1,912 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "nonposix.h" + +#define DEFAULT_LINEWIDTH 40 +static int linewidth = DEFAULT_LINEWIDTH; + +static int draw_flag = 1; + +/* These values were chosen because: + +(MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27) + +and 57816 is an exact multiple of both 72.27*SIZESCALE and 72. + +The width in the groff font file is the product of MULTIPLIER and the +width in the tfm file. */ + +#define RES 57816 +#define RES_7227 (RES/7227) +#define UNITWIDTH 131072 +#define SIZESCALE 100 +#define MULTIPLIER 1 + +#define FILL_MAX 1000 + +class dvi_font : public font { + dvi_font(const char *); +public: + int checksum; + int design_size; + ~dvi_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static dvi_font *load_dvi_font(const char *); +}; + +dvi_font *dvi_font::load_dvi_font(const char *s) +{ + dvi_font *f = new dvi_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +dvi_font::dvi_font(const char *nm) +: font(nm), checksum(0), design_size(0) +{ +} + +dvi_font::~dvi_font() +{ +} + +void dvi_font::handle_unknown_font_command(const char *command, + const char *arg, + const char *filename, int lineno) +{ + char *ptr; + if (strcmp(command, "checksum") == 0) { + if (arg == 0) + fatal_with_file_and_line(filename, lineno, + "`checksum' command requires an argument"); + checksum = int(strtol(arg, &ptr, 10)); + if (checksum == 0 && ptr == arg) { + fatal_with_file_and_line(filename, lineno, "bad checksum"); + } + } + else if (strcmp(command, "designsize") == 0) { + if (arg == 0) + fatal_with_file_and_line(filename, lineno, + "`designsize' command requires an argument"); + design_size = int(strtol(arg, &ptr, 10)); + if (design_size == 0 && ptr == arg) { + fatal_with_file_and_line(filename, lineno, "bad design size"); + } + } +} + +#define FONTS_MAX 256 + +struct output_font { + dvi_font *f; + int point_size; + output_font() : f(0) { } +}; + +class dvi_printer : public printer { + FILE *fp; + int max_drift; + int byte_count; + int last_bop; + int page_count; + int cur_h; + int cur_v; + int end_h; + int max_h; + int max_v; + output_font output_font_table[FONTS_MAX]; + font *cur_font; + int cur_point_size; + int pushed; + int pushed_h; + int pushed_v; + int have_pushed; + void preamble(); + void postamble(); + void define_font(int); + void set_font(int); + void possibly_begin_line(); +protected: + enum { + id_byte = 2, + set1 = 128, + put1 = 133, + put_rule = 137, + bop = 139, + eop = 140, + push = 141, + pop = 142, + right1 = 143, + down1 = 157, + fnt_num_0 = 171, + fnt1 = 235, + xxx1 = 239, + fnt_def1 = 243, + pre = 247, + post = 248, + post_post = 249, + filler = 223 + }; + int line_thickness; + + void out1(int); + void out2(int); + void out3(int); + void out4(int); + void moveto(int, int); + void out_string(const char *); + void out_signed(unsigned char, int); + void out_unsigned(unsigned char, int); + void do_special(const char *); +public: + dvi_printer(); + ~dvi_printer(); + font *make_font(const char *); + void begin_page(int); + void end_page(int); + void set_char(int, font *, const environment *, int w, const char *name); + void special(char *arg, const environment *env, char type); + void end_of_line(); + void draw(int code, int *p, int np, const environment *env); +}; + + +class draw_dvi_printer : public dvi_printer { + int output_pen_size; + int fill; + void set_line_thickness(const environment *); + void fill_next(); +public: + draw_dvi_printer(); + ~draw_dvi_printer(); + void draw(int code, int *p, int np, const environment *env); + void end_page(int); +}; + +dvi_printer::dvi_printer() +: fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0), + cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1) +{ + if (font::res != RES) + fatal("resolution must be %1", RES); + if (font::unitwidth != UNITWIDTH) + fatal("unitwidth must be %1", UNITWIDTH); + if (font::hor != 1) + fatal("hor must be equal to 1"); + if (font::vert != 1) + fatal("vert must be equal to 1"); + if (font::sizescale != SIZESCALE) + fatal("sizescale must be equal to %1", SIZESCALE); + max_drift = font::res/1000; // this is fairly arbitrary + preamble(); +} + +dvi_printer::~dvi_printer() +{ + postamble(); +} + + +draw_dvi_printer::draw_dvi_printer() +: output_pen_size(-1), fill(FILL_MAX) +{ +} + +draw_dvi_printer::~draw_dvi_printer() +{ +} + + +void dvi_printer::out1(int n) +{ + byte_count += 1; + putc(n & 0xff, fp); +} + +void dvi_printer::out2(int n) +{ + byte_count += 2; + putc((n >> 8) & 0xff, fp); + putc(n & 0xff, fp); +} + +void dvi_printer::out3(int n) +{ + byte_count += 3; + putc((n >> 16) & 0xff, fp); + putc((n >> 8) & 0xff, fp); + putc(n & 0xff, fp); +} + +void dvi_printer::out4(int n) +{ + byte_count += 4; + putc((n >> 24) & 0xff, fp); + putc((n >> 16) & 0xff, fp); + putc((n >> 8) & 0xff, fp); + putc(n & 0xff, fp); +} + +void dvi_printer::out_string(const char *s) +{ + out1(strlen(s)); + while (*s != 0) + out1(*s++); +} + + +void dvi_printer::end_of_line() +{ + if (pushed) { + out1(pop); + pushed = 0; + cur_h = pushed_h; + cur_v = pushed_v; + } +} + +void dvi_printer::possibly_begin_line() +{ + if (!pushed) { + have_pushed = pushed = 1; + pushed_h = cur_h; + pushed_v = cur_v; + out1(push); + } +} + +int scale(int x, int z) +{ + int sw; + int a, b, c, d; + int alpha, beta; + alpha = 16*z; beta = 16; + while (z >= 040000000L) { + z /= 2; beta /= 2; + } + d = x & 255; + c = (x >> 8) & 255; + b = (x >> 16) & 255; + a = (x >> 24) & 255; + sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta; + if (a == 255) + sw -= alpha; + else + assert(a == 0); + return sw; +} + + +void dvi_printer::set_char(int index, font *f, const environment *env, int w, const char *name) +{ + int code = f->get_code(index); + if (env->size != cur_point_size || f != cur_font) { + cur_font = f; + cur_point_size = env->size; + int i; + for (i = 0;; i++) { + if (i >= FONTS_MAX) { + fatal("too many output fonts required"); + } + if (output_font_table[i].f == 0) { + output_font_table[i].f = (dvi_font *)cur_font; + output_font_table[i].point_size = cur_point_size; + define_font(i); + } + if (output_font_table[i].f == cur_font + && output_font_table[i].point_size == cur_point_size) + break; + } + set_font(i); + } + int distance = env->hpos - cur_h; + if (env->hpos != end_h && distance != 0) { + out_signed(right1, distance); + cur_h = env->hpos; + } + else if (distance > max_drift) { + out_signed(right1, distance - max_drift); + cur_h = env->hpos - max_drift; + } + else if (distance < -max_drift) { + out_signed(right1, distance + max_drift); + cur_h = env->hpos + max_drift; + } + if (env->vpos != cur_v) { + out_signed(down1, env->vpos - cur_v); + cur_v = env->vpos; + } + possibly_begin_line(); + end_h = env->hpos + w; + cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER, + cur_point_size*RES_7227); + if (cur_h > max_h) + max_h = cur_h; + if (cur_v > max_v) + max_v = cur_v; + if (code >= 0 && code <= 127) + out1(code); + else + out_unsigned(set1, code); +} + +void dvi_printer::define_font(int i) +{ + out_unsigned(fnt_def1, i); + dvi_font *f = output_font_table[i].f; + out4(f->checksum); + out4(output_font_table[i].point_size*RES_7227); + out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5)); + const char *nm = f->get_internal_name(); + out1(0); + out_string(nm); +} + +void dvi_printer::set_font(int i) +{ + if (i >= 0 && i <= 63) + out1(fnt_num_0 + i); + else + out_unsigned(fnt1, i); +} + +void dvi_printer::out_signed(unsigned char base, int param) +{ + if (-128 <= param && param < 128) { + out1(base); + out1(param); + } + else if (-32768 <= param && param < 32768) { + out1(base+1); + out2(param); + } + else if (-(1 << 23) <= param && param < (1 << 23)) { + out1(base+2); + out3(param); + } + else { + out1(base+3); + out4(param); + } +} + +void dvi_printer::out_unsigned(unsigned char base, int param) +{ + if (param >= 0) { + if (param < 256) { + out1(base); + out1(param); + } + else if (param < 65536) { + out1(base+1); + out2(param); + } + else if (param < (1 << 24)) { + out1(base+2); + out3(param); + } + else { + out1(base+3); + out4(param); + } + } + else { + out1(base+3); + out4(param); + } +} + +void dvi_printer::preamble() +{ + out1(pre); + out1(id_byte); + out4(254000); + out4(font::res); + out4(1000); + out1(0); +} + +void dvi_printer::postamble() +{ + int tem = byte_count; + out1(post); + out4(last_bop); + out4(254000); + out4(font::res); + out4(1000); + out4(max_v); + out4(max_h); + out2(have_pushed); // stack depth + out2(page_count); + int i; + for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++) + define_font(i); + out1(post_post); + out4(tem); + out1(id_byte); + for (i = 0; i < 4 || byte_count % 4 != 0; i++) + out1(filler); +} + +void dvi_printer::begin_page(int i) +{ + page_count++; + int tem = byte_count; + out1(bop); + out4(i); + for (int j = 1; j < 10; j++) + out4(0); + out4(last_bop); + last_bop = tem; + // By convention position (0,0) in a dvi file is placed at (1in, 1in). + cur_h = font::res; + cur_v = font::res; + end_h = 0; +} + +void dvi_printer::end_page(int) +{ + if (pushed) + end_of_line(); + out1(eop); + cur_font = 0; +} + +void draw_dvi_printer::end_page(int len) +{ + dvi_printer::end_page(len); + output_pen_size = -1; +} + +void dvi_printer::do_special(const char *s) +{ + int len = strlen(s); + if (len == 0) + return; + possibly_begin_line(); + out_unsigned(xxx1, len); + while (*s) + out1(*s++); +} + +void dvi_printer::special(char *arg, const environment *env, char type) +{ + if (type != 'p') + return; + moveto(env->hpos, env->vpos); + do_special(arg); +} + +void dvi_printer::moveto(int h, int v) +{ + if (h != cur_h) { + out_signed(right1, h - cur_h); + cur_h = h; + if (cur_h > max_h) + max_h = cur_h; + } + if (v != cur_v) { + out_signed(down1, v - cur_v); + cur_v = v; + if (cur_v > max_v) + max_v = cur_v; + } + end_h = 0; +} + +void dvi_printer::draw(int code, int *p, int np, const environment *env) +{ + if (code == 'l') { + int x, y; + int height = 0, width; + int thickness; + if (line_thickness < 0) + thickness = env->size*RES_7227*linewidth/1000; + else if (line_thickness > 0) + thickness = line_thickness; + else + thickness = 1; + if (np != 2) { + error("2 arguments required for line"); + } + else if (p[0] == 0) { + // vertical rule + if (p[1] > 0) { + x = env->hpos - thickness/2; + y = env->vpos + p[1] + thickness/2; + height = p[1] + thickness; + width = thickness; + } + else if (p[1] < 0) { + x = env->hpos - thickness/2; + y = env->vpos + thickness/2; + height = thickness - p[1]; + width = thickness; + } + } + else if (p[1] == 0) { + if (p[0] > 0) { + x = env->hpos - thickness/2; + y = env->vpos + thickness/2; + height = thickness; + width = p[0] + thickness; + } + else if (p[0] < 0) { + x = env->hpos - p[0] - thickness/2; + y = env->vpos + thickness/2; + height = thickness; + width = thickness - p[0]; + } + } + if (height != 0) { + moveto(x, y); + out1(put_rule); + out4(height); + out4(width); + } + } + else if (code == 't') { + if (np == 0) { + line_thickness = -1; + } + else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) + error("0 or 1 argument required for thickness"); + else + line_thickness = p[0]; + } + } + else if (code == 'R') { + if (np != 2) + error("2 arguments required for rule"); + else if (p[0] != 0 || p[1] != 0) { + int dh = p[0]; + int dv = p[1]; + int oh = env->hpos; + int ov = env->vpos; + if (dv > 0) { + ov += dv; + dv = -dv; + } + if (dh < 0) { + oh += dh; + dh = -dh; + } + moveto(oh, ov); + out1(put_rule); + out4(-dv); + out4(dh); + } + } +} + +// XXX Will this overflow? + +inline int milliinches(int n) +{ + return (n*1000 + font::res/2)/font::res; +} + +void draw_dvi_printer::set_line_thickness(const environment *env) +{ + int desired_pen_size + = milliinches(line_thickness < 0 + // Will this overflow? + ? env->size*RES_7227*linewidth/1000 + : line_thickness); + if (desired_pen_size != output_pen_size) { + char buf[256]; + sprintf(buf, "pn %d", desired_pen_size); + do_special(buf); + output_pen_size = desired_pen_size; + } +} + +void draw_dvi_printer::fill_next() +{ + char buf[256]; + sprintf(buf, "sh %.3f", double(fill)/FILL_MAX); + do_special(buf); +} + +void draw_dvi_printer::draw(int code, int *p, int np, const environment *env) +{ + char buf[1024]; + int fill_flag = 0; + switch (code) { + case 'C': + fill_flag = 1; + // fall through + case 'c': + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + moveto(env->hpos+p[0]/2, env->vpos); + if (fill_flag) + fill_next(); + else + set_line_thickness(env); + int rad; + rad = milliinches(p[0]/2); + sprintf(buf, "%s 0 0 %d %d 0 6.28319", + (fill_flag ? "ia" : "ar"), + rad, + rad); + do_special(buf); + break; + case 'l': + if (np != 2) { + error("2 arguments required for line"); + break; + } + moveto(env->hpos, env->vpos); + set_line_thickness(env); + do_special("pa 0 0"); + sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1])); + do_special(buf); + do_special("fp"); + break; + case 'E': + fill_flag = 1; + // fall through + case 'e': + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + moveto(env->hpos+p[0]/2, env->vpos); + if (fill_flag) + fill_next(); + sprintf(buf, "%s 0 0 %d %d 0 6.28319", + (fill_flag ? "ia" : "ar"), + milliinches(p[0]/2), + milliinches(p[1]/2)); + do_special(buf); + break; + case 'P': + fill_flag = 1; + // fall through + case 'p': + { + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + moveto(env->hpos, env->vpos); + if (fill_flag) + fill_next(); + else + set_line_thickness(env); + do_special("pa 0 0"); + int h = 0, v = 0; + for (int i = 0; i < np; i += 2) { + h += p[i]; + v += p[i+1]; + sprintf(buf, "pa %d %d", milliinches(h), milliinches(v)); + do_special(buf); + } + do_special("pa 0 0"); + do_special(fill_flag ? "ip" : "fp"); + break; + } + case '~': + { + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + moveto(env->hpos, env->vpos); + set_line_thickness(env); + do_special("pa 0 0"); + int h = 0, v = 0; + for (int i = 0; i < np; i += 2) { + h += p[i]; + v += p[i+1]; + sprintf(buf, "pa %d %d", milliinches(h), milliinches(v)); + do_special(buf); + } + do_special("sp"); + break; + } + case 'a': + { + if (np != 4) { + error("4 arguments required for arc"); + break; + } + set_line_thickness(env); + double c[2]; + if (adjust_arc_center(p, c)) { + int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5)); + moveto(env->hpos + int(c[0]), env->vpos + int(c[1])); + sprintf(buf, "ar 0 0 %d %d %f %f", + rad, + rad, + atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]), + atan2(-c[1], -c[0])); + do_special(buf); + } + else { + moveto(env->hpos, env->vpos); + do_special("pa 0 0"); + sprintf(buf, + "pa %d %d", + milliinches(p[0] + p[2]), + milliinches(p[1] + p[3])); + do_special(buf); + do_special("fp"); + } + break; + } + case 't': + { + if (np == 0) { + line_thickness = -1; + } + else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + case 'f': + { + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) + fill = FILL_MAX; + break; + } + case 'R': + { + if (np != 2) { + error("2 arguments required for rule"); + break; + } + int dh = p[0]; + if (dh == 0) + break; + int dv = p[1]; + if (dv == 0) + break; + int oh = env->hpos; + int ov = env->vpos; + if (dv > 0) { + ov += dv; + dv = -dv; + } + if (dh < 0) { + oh += dh; + dh = -dh; + } + moveto(oh, ov); + out1(put_rule); + out4(-dv); + out4(dh); + break; + } + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } +} + +font *dvi_printer::make_font(const char *nm) +{ + return dvi_font::load_dvi_font(nm); +} + +printer *make_printer() +{ + if (draw_flag) + return new draw_dvi_printer; + else + return new dvi_printer; +} + +static void usage(FILE *stream); + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "F:vw:d", long_options, NULL)) != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + printf("GNU grodvi (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'w': + if (sscanf(optarg, "%d", &linewidth) != 1 + || linewidth < 0 || linewidth > 1000) { + error("bad line width"); + linewidth = DEFAULT_LINEWIDTH; + } + break; + case 'd': + draw_flag = 0; + break; + case 'F': + font::command_line_font_dir(optarg); + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } +#ifdef SET_BINARY + SET_BINARY(fileno(stdout)); +#endif + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n", + program_name); +} diff --git a/contrib/groff/src/devices/grodvi/grodvi.man b/contrib/groff/src/devices/grodvi/grodvi.man new file mode 100644 index 0000000..7aa4431 --- /dev/null +++ b/contrib/groff/src/devices/grodvi/grodvi.man @@ -0,0 +1,174 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X +.el .ds tx TeX +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GRODVI @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grodvi \- convert groff output to TeX dvi format +.SH SYNOPSIS +.B grodvi +[ +.B \-dv +] [ +.BI \-w n +] [ +.BI \-F dir +] [ +.IR files \|.\|.\|. +] +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +.B grodvi +is a driver for +.B groff +that produces \*(tx dvi format. +Normally it should be run by +.BR groff\ \-Tdvi . +This will run +.BR @g@troff\ \-Tdvi ; +it will also input the macros +.BR @MACRODIR@/dvi.tmac ; +if the input is being preprocessed with +.B @g@eqn +it will also input +.BR @FONTDIR@/devdvi/eqnchar . +.LP +The dvi file generated by +.B grodvi +can be printed by any correctly-written dvi driver. +The troff drawing primitives are implemented +using the tpic version 2 specials. +If the driver does not support these, the +.B \eD +commands will not produce any output. +.LP +There is an additional drawing command available: +.TP +.BI \eD'R\ dh\ dv ' +Draw a rule (solid black rectangle), with one corner +at the current position, and the diagonally opposite corner +at the current position +.RI +( dh , dv ). +Afterwards the current position will be at the opposite corner. This +produces a rule in the dvi file and so can be printed even with a +driver that does not support the tpic specials unlike the other +.B \eD +commands. +.LP +The groff command +.BI \eX' anything ' +is translated into the same command in the dvi file as would be +produced by +.BI \especial{ anything } +in \*(tx; +.I anything may not contain a newline. +.LP +Font files for +.B grodvi +can be created from tfm files using +.BR tfmtodit (@MAN1EXT@). +The font description file should contain the following +additional commands: +.Tp \w'\fBinternalname'u+2n +.BI internalname\ name +The name of the tfm file (without the +.B .tfm +extension) is +.IR name . +.TP +.BI checksum\ n +The checksum in the tfm file is +.IR n . +.TP +.BI designsize\ n +The designsize in the tfm file is +.IR n . +.LP +These are automatically generated by +.B tfmtodit. +.LP +In +.B troff +the +.B \eN +escape sequence can be used to access characters by their position +in the corresponding tfm file; +all characters in the tfm file can be accessed this way. +.SH OPTIONS +.TP +.B \-d +Do not use tpic specials to implement drawing commands. +Horizontal and vertical lines will be implemented by rules. +Other drawing commands will be ignored. +.TP +.B \-v +Print the version number. +.TP +.BI \-w n +Set the default line thickness to +.I n +thousandths of an em. +.TP +.BI \-F dir +Prepend directory +.IB dir /devdvi +to the search path for font and device description files. +.SH FILES +.TP +.B @FONTDIR@/devdvi/DESC +Device description file. +.TP +.BI @FONTDIR@/devdvi/ F +Font description file for font +.IR F . +.TP +.B @MACRODIR@/dvi.tmac +Macros for use with +.BR grodvi . +.SH BUGS +Dvi files produced by +.B grodvi +use a different resolution (57816 units per inch) to those produced by +\*(tx. +Incorrectly written drivers which assume the resolution used by \*(tx, +rather than using the resolution specified in the dvi file will not +work with +.BR grodvi . +.LP +When using the +.B \-d +option with boxed tables, +vertical and horizontal lines can sometimes protrude by one pixel. +This is a consequence of the way \*(tx requires that the heights +and widths of rules be rounded. +.SH "SEE ALSO" +.BR tfmtodit (@MAN1EXT@), +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/src/devices/grohtml/Makefile.sub b/contrib/groff/src/devices/grohtml/Makefile.sub new file mode 100644 index 0000000..2c3a55a --- /dev/null +++ b/contrib/groff/src/devices/grohtml/Makefile.sub @@ -0,0 +1,16 @@ +PROG=post-grohtml +MAN1=grohtml.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=\ + post-html.o \ + html-text.o \ + output.o +CCSRCS=\ + $(srcdir)/post-html.cc \ + $(srcdir)/html-text.cc \ + $(srcdir)/output.cc +HDRS=\ + $(srcdir)/html.h \ + $(srcdir)/html-chars.h \ + $(srcdir)/html-text.h diff --git a/contrib/groff/src/devices/grohtml/grohtml.man b/contrib/groff/src/devices/grohtml/grohtml.man new file mode 100644 index 0000000..8796d8f --- /dev/null +++ b/contrib/groff/src/devices/grohtml/grohtml.man @@ -0,0 +1,137 @@ +.ig \"-*- nroff -*- +Copyright (C) 1999-2000, 2001 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROHTML @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grohtml \- html driver for groff +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fBgrohtml 'u +.ti \niu +.B grohtml +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-v?lrn +.OP \-F dir +.OP \-i resolution +.OP \-o image vertical offset +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.SH DESCRIPTION +.B grohtml +translates the output of GNU +.B troff +to html. +Users should always invoke +.B grohtml +via the groff command with a +.B \-Thtml +option. +If no files are given, +.B grohtml +will read the standard input. +A filename of +.B \- +will also cause +.B grohtml +to read the standard input. +Html output is written to the standard output. +When +.B grohtml +is run by +.B groff +options can be passed to +.B grohtml +using +.BR groff 's +.B \-P +option. +.SH OPTIONS +.TP +.B \-v +Displays the version. +.TP +.B \-? +Emits a usage synopsis. +.TP +.B -l +Turns off the production of automatic section links at the top of the document. +.TP +.B -r +Turns off the automatic header and footer line (html rule). +.TP +.B -n +Generate simple heading anchors whenever a section/number heading is found. +Without the option the anchor value is the textual heading. This can cause problems +when a heading contains a `?' on some brousers (netscape). +This flag is automatically turned on if a heading contains an image. +.TP +.BI \-F dir +Prepend directory +.IB dir /dev name +to the search path for font and device description files; +.I name +is the name of the device, usually +.BR html . +.TP +.BI \-i resolution +select the resolution for all images. +By default this is 80 pixels per inch. +Example: -i100 indicates 100 pixels per inch. +.TP +.B \-v +Print the version number. +.TP +.B \-? +Display usage. +.SH USAGE +There are styles called +.BR R , +.BR I , +.BR B , +and +.B BI +mounted at font positions 1 to 4. +.SH DEPENDENCIES +.B grohtml +is dependent upon the png utilities and gs. +Images are generated whenever a table, picture, equation or line is +encountered. +.SH BUGS +.B Grohtml +has been completely redesigned and rewritten. +It is still alpha code. +.SH "SEE ALSO" +.BR afmtodit (@MAN1EXT@), +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR psbb (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/src/devices/grohtml/html-chars.h b/contrib/groff/src/devices/grohtml/html-chars.h new file mode 100644 index 0000000..f58f8dc --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-chars.h @@ -0,0 +1,27 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + * + * html-chars.h + * + * provides a diacritical character combination table for html + */ + + + +struct diacritical_desc { + char *mark; + char *second_troff_char; + char translation; +}; + + +static struct diacritical_desc diacritical_table[] = { + { "ad" , "aeiouyAEIOU" , ':' , }, /* */ + { "ac" , "cC" , ',' , }, /* cedilla */ + { "aa" , "aeiouyAEIOU" , '\'' , }, /* acute */ + { NULL , NULL , (char)0, }, +}; diff --git a/contrib/groff/src/devices/grohtml/html-text.cc b/contrib/groff/src/devices/grohtml/html-text.cc new file mode 100644 index 0000000..0b63aa0 --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-text.cc @@ -0,0 +1,829 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.cc + * + * provide a troff like state machine interface which + * generates html text. + */ + +/* +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + + +#include "html-text.h" + + +html_text::html_text (simple_output *op) : + stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE), + current_indentation(-1), pageoffset(-1), linelength(-1) +{ +} + +html_text::~html_text () +{ + flush_text(); +} + +/* + * end_tag - shuts down the tag. + */ + +void html_text::end_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: out->put_string("</i>"); break; + case B_TAG: out->put_string("</b>"); break; + case P_TAG: out->put_string("</p>").nl().enable_newlines(FALSE); break; + case SUB_TAG: out->put_string("</sub>"); break; + case SUP_TAG: out->put_string("</sup>"); break; + case TT_TAG: out->put_string("</tt>"); break; + case PRE_TAG: out->put_string("</pre>"); + if (! is_present(TABLE_TAG)) { + out->nl(); + out->enable_newlines(TRUE); + } + break; + case SMALL_TAG: out->put_string("</small>"); break; + case BIG_TAG: out->put_string("</big>"); break; + case TABLE_TAG: issue_table_end(); break; + + default: + error("unrecognised tag"); + } +} + +/* + * issue_tag - writes out an html tag with argument. + */ + +void html_text::issue_tag (char *tagname, char *arg) +{ + if ((arg == 0) || (strlen(arg) == 0)) { + out->put_string(tagname); + out->put_string(">"); + } else { + out->put_string(tagname); + out->put_string(" "); + out->put_string(arg); + out->put_string(">"); + } +} + +/* + * start_tag - starts a tag. + */ + +void html_text::start_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: issue_tag("<i", t->arg1); break; + case B_TAG: issue_tag("<b", t->arg1); break; + case P_TAG: issue_tag("\n<p", t->arg1); + out->enable_newlines(TRUE); break; + case SUB_TAG: issue_tag("<sub", t->arg1); break; + case SUP_TAG: issue_tag("<sup", t->arg1); break; + case TT_TAG: issue_tag("<tt", t->arg1); break; + case PRE_TAG: out->nl(); issue_tag("<pre", t->arg1); + out->enable_newlines(FALSE); break; + case SMALL_TAG: issue_tag("<small", t->arg1); break; + case BIG_TAG: issue_tag("<big", t->arg1); break; + case TABLE_TAG: issue_table_begin(t); break; + case BREAK_TAG: break; + + default: + error("unrecognised tag"); + } +} + +int html_text::table_is_void (tag_definition *t) +{ + if (linelength > 0) { + return( current_indentation*100/linelength <= 0 ); + } else { + return( FALSE ); + } +} + +void html_text::issue_table_begin (tag_definition *t) +{ + if (linelength > 0) { + int width=current_indentation*100/linelength; + + if (width > 0) { + out->put_string("<table width=\"100%\" border=0 rules=\"none\" frame=\"void\"\n cols=\"2\" cellspacing=\"0\" cellpadding=\"0\">").nl(); + out->put_string("<tr valign=\"top\" align=\"left\">").nl(); + if ((t->arg1 == 0) || (strcmp(t->arg1, "") == 0)) + out->put_string("<td width=\"").put_number(width).put_string("%\"></td>"); + else { + out->put_string("<td width=\"").put_number(width).put_string("%\">").nl(); + out->put_string(t->arg1).put_string("</td>"); + t->arg1[0] = (char)0; + } + out->put_string("<td width=\"").put_number(100-width).put_string("%\">").nl(); + } + } +} + +void html_text::issue_table_end (void) +{ + out->put_string("</td></table>").nl(); + out->enable_newlines(TRUE); +} + +/* + * flush_text - flushes html tags which are outstanding on the html stack. + */ + +void html_text::flush_text (void) +{ + int notext=TRUE; + tag_definition *p=stackptr; + + while (stackptr != 0) { + notext = (notext && (! stackptr->text_emitted)); + if (! notext) { + end_tag(stackptr); + } + p = stackptr; + stackptr = stackptr->next; + free(p); + } + lastptr = NULL; +} + +/* + * is_present - returns TRUE if tag is already present on the stack. + */ + +int html_text::is_present (HTML_TAG t) +{ + tag_definition *p=stackptr; + + while (p != NULL) { + if (t == p->type) { + return( TRUE ); + } + p = p->next; + } + return( FALSE ); +} + +/* + * push_para - adds a new entry onto the html paragraph stack. + */ + +void html_text::push_para (HTML_TAG t, char *arg) +{ + tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition)); + + p->type = t; + p->arg1 = arg; + p->text_emitted = FALSE; + + /* + * if t is a P_TAG or TABLE_TAG or PRE_TAG make sure it goes on the end of the stack. + * But we insist that a TABLE_TAG is always after a PRE_TAG + * and that a P_TAG is always after a TABLE_TAG + */ + + if (((t == P_TAG) || (t == PRE_TAG) || (t == TABLE_TAG)) && + (lastptr != NULL)) { + if (((lastptr->type == TABLE_TAG) && (t == PRE_TAG)) || + ((lastptr->type == P_TAG) && (t == TABLE_TAG))) { + /* + * insert p before the lastptr + */ + if (stackptr == lastptr) { + /* + * only on element of the stack + */ + p->next = stackptr; + stackptr = p; + } else { + /* + * more than one element is on the stack + */ + tag_definition *q = stackptr; + + while (q->next != lastptr) { + q = q->next; + } + q->next = p; + p->next = lastptr; + } + } else { + /* + * store, p, at the end + */ + lastptr->next = p; + lastptr = p; + p->next = NULL; + } + } else { + p->next = stackptr; + if (stackptr == NULL) + lastptr = p; + stackptr = p; + } +} + +/* + * do_indent - remember the indent parameters and if + * indent is > pageoff and indent has changed + * then we start a html table to implement the indentation. + */ + +void html_text::do_indent (char *arg, int indent, int pageoff, int linelen) +{ + if ((current_indentation != -1) && + (pageoffset+current_indentation != indent+pageoff)) { + /* + * actual indentation of text has changed, we need to put + * a table tag onto the stack. + */ + do_table(arg); + } + current_indentation = indent; + pageoffset = pageoff; + linelength = linelen; +} + +void html_text::do_table (char *arg) +{ + int in_pre = is_in_pre(); + // char *para_type = done_para(); + done_pre(); + shutdown(TABLE_TAG); // shutdown a previous table, if present + remove_break(); + if (in_pre) { + do_pre(); + } + // do_para(para_type); + push_para(TABLE_TAG, arg); +} + +/* + * done_table - terminates a possibly existing table. + */ + +void html_text::done_table (void) +{ + shutdown(TABLE_TAG); + space_emitted = TRUE; +} + +/* + * do_italic - changes to italic + */ + +void html_text::do_italic (void) +{ + done_bold(); + done_tt(); + if (! is_present(I_TAG)) { + push_para(I_TAG, ""); + } +} + +/* + * do_bold - changes to bold. + */ + +void html_text::do_bold (void) +{ + done_italic(); + done_tt(); + if (! is_present(B_TAG)) { + push_para(B_TAG, ""); + } +} + +/* + * do_tt - changes to teletype. + */ + +void html_text::do_tt (void) +{ + done_bold(); + done_italic(); + if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG))) { + push_para(TT_TAG, ""); + } +} + +/* + * do_pre - changes to preformated text. + */ + +void html_text::do_pre (void) +{ + done_bold(); + done_italic(); + done_tt(); + char *type = done_para(); + if (! is_present(PRE_TAG)) { + push_para(PRE_TAG, ""); + } +} + +/* + * is_in_pre - returns TRUE if we are currently within a preformatted + * <pre> block. + */ + +int html_text::is_in_pre (void) +{ + return( is_present(PRE_TAG) ); +} + +/* + * is_in_table - returns TRUE if we are currently within a table. + */ + +int html_text::is_in_table (void) +{ + return( is_present(TABLE_TAG) ); +} + +/* + * shutdown - shuts down an html tag. + */ + +char *html_text::shutdown (HTML_TAG t) +{ + char *arg=NULL; + + if (is_present(t)) { + tag_definition *p =stackptr; + tag_definition *temp =NULL; + int notext =TRUE; + + while ((stackptr != NULL) && (stackptr->type != t)) { + notext = (notext && (! stackptr->text_emitted)); + if (! notext) { + end_tag(stackptr); + } + + /* + * pop tag + */ + p = stackptr; + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + + /* + * push tag onto temp stack + */ + p->next = temp; + temp = p; + } + + /* + * and examine stackptr + */ + if ((stackptr != NULL) && (stackptr->type == t)) { + if (stackptr->text_emitted) { + end_tag(stackptr); + } + if (t == P_TAG) { + arg = stackptr->arg1; + } + p = stackptr; + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + free(p); + } + + /* + * and restore unaffected tags + */ + while (temp != NULL) { + push_para(temp->type, temp->arg1); + p = temp; + temp = temp->next; + free(p); + } + } + return( arg ); +} + +/* + * done_bold - shuts downs a bold tag. + */ + +void html_text::done_bold (void) +{ + shutdown(B_TAG); +} + +/* + * done_italic - shuts downs an italic tag. + */ + +void html_text::done_italic (void) +{ + shutdown(I_TAG); +} + +/* + * done_sup - shuts downs a sup tag. + */ + +void html_text::done_sup (void) +{ + shutdown(SUP_TAG); +} + +/* + * done_sub - shuts downs a sub tag. + */ + +void html_text::done_sub (void) +{ + shutdown(SUB_TAG); +} + +/* + * done_tt - shuts downs a tt tag. + */ + +void html_text::done_tt (void) +{ + shutdown(TT_TAG); +} + +/* + * done_pre - shuts downs a pre tag. + */ + +void html_text::done_pre (void) +{ + shutdown(PRE_TAG); +} + +/* + * done_small - shuts downs a small tag. + */ + +void html_text::done_small (void) +{ + shutdown(SMALL_TAG); +} + +/* + * done_big - shuts downs a big tag. + */ + +void html_text::done_big (void) +{ + shutdown(BIG_TAG); +} + +/* + * check_emit_text - ensures that all previous tags have been emitted (in order) + * before the text is written. + */ + +void html_text::check_emit_text (tag_definition *t) +{ + if ((t != NULL) && (! t->text_emitted)) { + /* + * we peep and see whether there is a <p> before the <table> + * in which case we skip the <p> + */ + if (t->type == TABLE_TAG) { + if (table_is_void(t)) { + tag_definition *n = t->next; + remove_def(t); + check_emit_text(n); + } else { + /* + * a table which will be emitted, is there a <p> succeeding it? + */ + if ((t->next != NULL) && + (t->next->type == P_TAG) && + ((t->next->arg1 == 0) || strcmp(t->next->arg1, "") == 0)) { + /* + * yes skip the <p> + */ + check_emit_text(t->next->next); + } else { + check_emit_text(t->next); + } + t->text_emitted = TRUE; + start_tag(t); + } + } else { + check_emit_text(t->next); + t->text_emitted = TRUE; + start_tag(t); + } + } +} + +/* + * do_emittext - tells the class that text was written during the current tag. + */ + +void html_text::do_emittext (char *s, int length) +{ + if ((! is_present(P_TAG)) && (! is_present(PRE_TAG))) + do_para(""); + + if (is_present(BREAK_TAG)) { + int text = remove_break(); + check_emit_text(stackptr); + if (text) { + if (is_present(PRE_TAG)) { + out->nl(); + } else { + out->put_string("<br>").nl(); + } + } + } else { + check_emit_text(stackptr); + } + out->put_string(s, length); + space_emitted = FALSE; +} + +/* + * do_para- starts a new paragraph + */ + +void html_text::do_para (char *arg) +{ + done_pre(); + if (! is_present(P_TAG)) { + remove_sub_sup(); + if ((arg != 0) && (strcmp(arg, "") != 0)) { + remove_tag(TABLE_TAG); + } + push_para(P_TAG, arg); + space_emitted = TRUE; + } +} + +/* + * done_para - shuts down a paragraph tag. + */ + +char *html_text::done_para (void) +{ + space_emitted = TRUE; + return( shutdown(P_TAG) ); +} + +/* + * do_space - issues an end of paragraph + */ + +void html_text::do_space (void) +{ + if (is_in_pre()) { + do_emittext("", 0); + } else { + do_para(done_para()); + } + space_emitted = TRUE; +} + +/* + * do_break - issue a break tag. + */ + +void html_text::do_break (void) +{ + if (! is_present(PRE_TAG)) { + if (emitted_text()) { + if (! is_present(BREAK_TAG)) { + push_para(BREAK_TAG, ""); + } + } + } + space_emitted = TRUE; +} + +/* + * do_newline - issue a newline providing that we are inside a <pre> tag. + */ + +void html_text::do_newline (void) +{ + if (is_present(PRE_TAG)) { + do_emittext("\n", 1); + space_emitted = TRUE; + } +} + +/* + * emitted_text - returns FALSE if white space has just been written. + */ + +int html_text::emitted_text (void) +{ + return( ! space_emitted); +} + +/* + * emit_space - writes a space providing that text was written beforehand. + */ + +int html_text::emit_space (void) +{ + if (space_emitted) { + if (is_present(PRE_TAG)) { + do_emittext(" ", 1); + } + } else { + out->space_or_newline(); + space_emitted = TRUE; + } +} + +/* + * remove_def - removes a definition, t, from the stack. + */ + +void html_text::remove_def (tag_definition *t) +{ + tag_definition *p = stackptr; + tag_definition *l = 0; + tag_definition *q = 0; + + while ((p != 0) && (p != t)) { + l = p; + p = p->next; + } + if ((p != 0) && (p == t)) { + if (p == stackptr) { + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + q = stackptr; + } else if (l == 0) { + error("stack list pointers are wrong"); + } else { + l->next = p->next; + q = p->next; + if (l->next == NULL) + lastptr = l; + } + free(p); + } +} + +/* + * remove_tag - removes a tag from the stack. + */ + +void html_text::remove_tag (HTML_TAG tag) +{ + tag_definition *p = stackptr; + + while ((p != 0) && (p->type != tag)) { + p = p->next; + } + if ((p != 0) && (p->type == tag)) + remove_def(p); +} + +/* + * remove_sub_sup - removes a sub or sup tag, should either exist on the stack. + */ + +void html_text::remove_sub_sup (void) +{ + if (is_present(SUB_TAG)) { + remove_tag(SUB_TAG); + } + if (is_present(SUP_TAG)) { + remove_tag(SUP_TAG); + } + if (is_present(PRE_TAG)) { + remove_tag(PRE_TAG); + } +} + +/* + * remove_break - break tags are not balanced thus remove it once it has been emitted. + * It returns TRUE if text was emitted before the <br> was issued. + */ + +int html_text::remove_break (void) +{ + tag_definition *p = stackptr; + tag_definition *l = 0; + tag_definition *q = 0; + + while ((p != 0) && (p->type != BREAK_TAG)) { + l = p; + p = p->next; + } + if ((p != 0) && (p->type == BREAK_TAG)) { + if (p == stackptr) { + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + q = stackptr; + } else if (l == 0) { + error("stack list pointers are wrong"); + } else { + l->next = p->next; + q = p->next; + if (l->next == NULL) + lastptr = l; + } + free(p); + } + /* + * now determine whether text was issued before <br> + */ + while (q != 0) { + if (q->text_emitted) { + return( TRUE ); + } else { + q = q->next; + } + } + return( FALSE ); +} + +/* + * do_small - potentially inserts a <small> tag into the html stream. + * However we check for a <big> tag, if present then we terminate it. + * Otherwise a <small> tag is inserted. + */ + +void html_text::do_small (void) +{ + if (is_present(BIG_TAG)) { + done_big(); + } else { + push_para(SMALL_TAG, ""); + } +} + +/* + * do_big - is the mirror image of do_small. + */ + +void html_text::do_big (void) +{ + if (is_present(SMALL_TAG)) { + done_small(); + } else { + push_para(BIG_TAG, ""); + } +} + +/* + * do_sup - save a superscript tag on the stack of tags. + */ + +void html_text::do_sup (void) +{ + push_para(SUP_TAG, ""); +} + +/* + * do_sub - save a subscript tag on the stack of tags. + */ + +void html_text::do_sub (void) +{ + push_para(SUB_TAG, ""); +} + diff --git a/contrib/groff/src/devices/grohtml/html-text.h b/contrib/groff/src/devices/grohtml/html-text.h new file mode 100644 index 0000000..c8ab2ac --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-text.h @@ -0,0 +1,109 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.h + * + * provides a state machine interface which generates html text. + */ + +/* +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "html.h" + +/* + * html tags + */ + +typedef enum {I_TAG, B_TAG, P_TAG, SUB_TAG, SUP_TAG, TT_TAG, + PRE_TAG, SMALL_TAG, BIG_TAG, BREAK_TAG, TABLE_TAG} HTML_TAG; + +typedef struct tag_definition { + HTML_TAG type; + char *arg1; + int text_emitted; + tag_definition *next; +} tag_definition ; + +/* + * the state of the current paragraph. + * It allows post-html.cc to request font changes, paragraph start/end + * and emits balanced tags with a small amount of peephole optimization. + */ + +class html_text { +public: + html_text (simple_output *op); + ~html_text (void); + void flush_text (void); + void do_emittext (char *s, int length); + void do_italic (void); + void do_bold (void); + void do_roman (void); + void do_tt (void); + void do_pre (void); + void do_small (void); + void do_big (void); + void do_para (char *arg1); + void do_sup (void); + void do_sub (void); + void do_space (void); + void do_break (void); + void do_newline (void); + void do_table (char *arg); + void done_bold (void); + void done_italic (void); + char *done_para (void); + void done_sup (void); + void done_sub (void); + void done_tt (void); + void done_pre (void); + void done_small (void); + void done_big (void); + void do_indent (char *arg, int indent, int pageoff, int linelen); + int emitted_text (void); + int emit_space (void); + int is_in_pre (void); + void remove_tag (HTML_TAG tag); + void remove_sub_sup (void); + void done_table (void); + int is_in_table (void); + +private: + tag_definition *stackptr; /* the current paragraph state */ + tag_definition *lastptr; /* the end of the stack */ + simple_output *out; + int space_emitted; + int current_indentation; /* current .in value */ + int pageoffset; /* .po value */ + int linelength; /* current line length */ + + int is_present (HTML_TAG t); + void end_tag (tag_definition *t); + void start_tag (tag_definition *t); + void push_para (HTML_TAG t, char *arg); + char *shutdown (HTML_TAG t); + void check_emit_text (tag_definition *t); + int remove_break (void); + void issue_tag (char *tagname, char *arg); + void issue_table_begin (tag_definition *t); + void issue_table_end (void); + int table_is_void (tag_definition *t); + void remove_def (tag_definition *t); +}; diff --git a/contrib/groff/src/devices/grohtml/html.h b/contrib/groff/src/devices/grohtml/html.h new file mode 100644 index 0000000..69b6e35 --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html.h @@ -0,0 +1,96 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if !defined(HTML_H) +# define HTML_H +# undef DEBUGGING +// # define DEBUGGING + +/* + * class and structure needed to buffer words + */ + +struct word { + char *s; + word *next; + + word (const char *w, int n); + ~word (); +}; + +class word_list { +public: + word_list (); + int flush (FILE *f); + void add_word (const char *s, int n); + int get_length (void); + +private: + int length; + word *head; + word *tail; +}; + +class simple_output { +public: + simple_output(FILE *, int max_line_length); + simple_output &put_string(const char *, int); + simple_output &put_string(const char *s); + simple_output &put_troffps_char (const char *s); + simple_output &put_translated_string(const char *s); + simple_output &put_number(int); + simple_output &put_float(double); + simple_output &put_symbol(const char *); + simple_output &put_literal_symbol(const char *); + simple_output &set_fixed_point(int); + simple_output &simple_comment(const char *); + simple_output &begin_comment(const char *); + simple_output &comment_arg(const char *); + simple_output &end_comment(); + simple_output &set_file(FILE *); + simple_output &include_file(FILE *); + simple_output ©_file(FILE *); + simple_output &end_line(); + simple_output &put_raw_char(char); + simple_output &special(const char *); + simple_output &enable_newlines(int); + simple_output &check_newline(int n); + simple_output &nl(void); + simple_output &space_or_newline (void); + simple_output &begin_tag (void); + FILE *get_file(); +private: + FILE *fp; + int max_line_length; // not including newline + int col; + int fixed_point; + int newlines; // can we issue newlines automatically? + word_list last_word; + + void flush_last_word (void); + int check_space (const char *s, int n); +}; + +inline FILE *simple_output::get_file() +{ + return fp; +} + +#endif diff --git a/contrib/groff/src/devices/grohtml/output.cc b/contrib/groff/src/devices/grohtml/output.cc new file mode 100644 index 0000000..4c72bba --- /dev/null +++ b/contrib/groff/src/devices/grohtml/output.cc @@ -0,0 +1,335 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + * + * output.cc + * + * provide the simple low level output routines needed by html.cc + */ + +/* +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#include <time.h> +#include "html.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +/* + * word - initialise a word and set next to NULL + */ + +word::word (const char *w, int n) + : next(0) +{ + s = (char *)malloc(n+1); + strncpy(s, w, n); + s[n] = (char)0; +} + +/* + * destroy word and the string copy. + */ + +word::~word () +{ + free(s); +} + +/* + * word_list - create an empty word list. + */ + +word_list::word_list () + : head(0), tail(0), length(0) +{ +} + +/* + * flush - flush a word list to a FILE, f, and return the + * length of the buffered string. + */ + +int word_list::flush (FILE *f) +{ + word *t; + int len=length; + + while (head != 0) { + t = head; + head = head->next; + fputs(t->s, f); + delete t; + } + head = 0; + tail = 0; + length = 0; +#if defined(DEBUGGING) + fflush(f); // just for testing +#endif + return( len ); +} + +/* + * add_word - adds a word to the outstanding word list. + */ + +void word_list::add_word (const char *s, int n) +{ + if (head == 0) { + head = new word(s, n); + tail = head; + } else { + tail->next = new word(s, n); + tail = tail->next; + } + length += n; +} + +/* + * get_length - returns the number of characters buffered + */ + +int word_list::get_length (void) +{ + return( length ); +} + +/* + * the classes and methods for simple_output manipulation + */ + +simple_output::simple_output(FILE *f, int n) +: fp(f), max_line_length(n), col(0), fixed_point(0), newlines(0) +{ +} + +simple_output &simple_output::set_file(FILE *f) +{ + if (fp) + fflush(fp); + fp = f; + return *this; +} + +simple_output &simple_output::copy_file(FILE *infp) +{ + int c; + while ((c = getc(infp)) != EOF) + putc(c, fp); + return *this; +} + +simple_output &simple_output::end_line() +{ + flush_last_word(); + if (col != 0) { + putc('\n', fp); + col = 0; + } + return *this; +} + +simple_output &simple_output::special(const char *s) +{ + return *this; +} + +simple_output &simple_output::simple_comment(const char *s) +{ + flush_last_word(); + if (col != 0) + putc('\n', fp); + fputs("<!-- ", fp); + fputs(s, fp); + fputs(" -->\n", fp); + col = 0; + return *this; +} + +simple_output &simple_output::begin_comment(const char *s) +{ + flush_last_word(); + if (col != 0) + putc('\n', fp); + col = 0; + put_string("<!--"); + space_or_newline(); + last_word.add_word(s, strlen(s)); + return *this; +} + +simple_output &simple_output::end_comment() +{ + flush_last_word(); + space_or_newline(); + put_string("-->").nl(); + return *this; +} + +/* + * check_newline - checks to see whether we are able to issue + * a newline and that one is needed. + */ + +simple_output &simple_output::check_newline(int n) +{ + if ((col + n + last_word.get_length() + 1 > max_line_length) && (newlines)) { + fputc('\n', fp); + col = last_word.flush(fp); + } +} + +/* + * space_or_newline - will emit a newline or a space later on + * depending upon the current column. + */ + +simple_output &simple_output::space_or_newline (void) +{ +#if defined(DEBUGGING) + fflush(fp); // just for testing +#endif + if ((col + last_word.get_length() + 1 > max_line_length) && (newlines)) { + fputc('\n', fp); + if (last_word.get_length() > 0) { + col = last_word.flush(fp); + } else { + col = 0; + } + } else { + if (last_word.get_length() != 0) { + if (col > 0) { + fputc(' ', fp); + col++; + } + col += last_word.flush(fp); + } + } +} + +/* + * nl - writes a newline providing that we + * are not in the first column. + */ + +simple_output &simple_output::nl (void) +{ + space_or_newline(); + col += last_word.flush(fp); + if (col != 0) { + fputc('\n', fp); + col = 0; + } + return *this ; +} + +simple_output &simple_output::set_fixed_point(int n) +{ + assert(n >= 0 && n <= 10); + fixed_point = n; + return *this; +} + +simple_output &simple_output::put_raw_char(char c) +{ + col += last_word.flush(fp); + putc(c, fp); + col++; + return *this; +} + +simple_output &simple_output::put_string(const char *s, int n) +{ + last_word.add_word(s, n); + return *this; +} + +simple_output &simple_output::put_string(const char *s) +{ + last_word.add_word(s, strlen(s)); + return *this; +} + +simple_output &simple_output::put_number(int n) +{ + char buf[1 + INT_DIGITS + 1]; + sprintf(buf, "%d", n); + put_string(buf); + return *this; +} + +simple_output &simple_output::put_float(double d) +{ + char buf[128]; + + sprintf(buf, "%.4f", d); + put_string(buf); + return *this; +} + +simple_output &simple_output::enable_newlines (int auto_newlines) +{ + check_newline(0); + newlines = auto_newlines; + check_newline(0); +} + +/* + * flush_last_word - flushes the last word and adjusts the + * col position. It will insert a newline + * before the last word if allowed and if + * necessary. + */ + +void simple_output::flush_last_word (void) +{ + int len=last_word.get_length(); + + if (len > 0) { + if (newlines) { + if (col + len + 1 > max_line_length) { + fputs("\n", fp); + col = 0; + } else { + fputs(" ", fp); + col++; + } + len += last_word.flush(fp); + } else { + fputs(" ", fp); + col++; + col += last_word.flush(fp); + } + } +} diff --git a/contrib/groff/src/devices/grohtml/post-html.cc b/contrib/groff/src/devices/grohtml/post-html.cc new file mode 100644 index 0000000..0237bfc --- /dev/null +++ b/contrib/groff/src/devices/grohtml/post-html.cc @@ -0,0 +1,2933 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + */ + +/* +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" +#include "html.h" +#include "html-chars.h" +#include "html-text.h" + +#include <time.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <stdio.h> +#include <fcntl.h> + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +#define MAX_STRING_LENGTH 4096 +#define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */ +#define SIZE_INCREMENT 2 /* font size increment <big> = +2 */ +#define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */ +#define CENTER_TOLERANCE 2 /* how many pixels off center will we still */ +#define ANCHOR_TEMPLATE "heading%d" /* if simple anchor is set we use this */ +#define UNICODE_DESC_START 0x80 /* all character entities above this are */ + /* either encoded by their glyph names or if */ + /* there is no name then we use &#nnn; */ +#define INDENTATION /* #undef INDENTATION to remove .in handling */ + +typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT; + +/* + * prototypes + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single); +char *get_html_translation (font *f, const char *name); + + +static int auto_links = TRUE; /* by default we enable automatic links at */ + /* top of the document. */ +static int auto_rule = TRUE; /* by default we enable an automatic rule */ + /* at the top and bottom of the document */ +static int simple_anchors = FALSE; /* default to anchors with heading text */ + + +/* + * start with a few favorites + */ + +void stop () {} + +static int min (int a, int b) +{ + if (a < b) { + return( a ); + } else { + return( b ); + } +} + +static int max (int a, int b) +{ + if (a > b) { + return( a ); + } else { + return( b ); + } +} + +/* + * is_subsection - returns TRUE if a1..a2 is within b1..b2 + */ + +static int is_subsection (int a1, int a2, int b1, int b2) +{ + // easier to see whether this is not the case + return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) ); +} + +/* + * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2 + */ + +static int is_intersection (int a1, int a2, int b1, int b2) +{ + // again easier to prove NOT outside limits + return( ! ((a1 > b2) || (a2 < b1)) ); +} + +/* + * is_digit - returns TRUE if character, ch, is a digit. + */ + +static int is_digit (char ch) +{ + return( (ch >= '0') && (ch <= '9') ); +} + +/* + * the classes and methods for maintaining a list of files. + */ + +struct file { + FILE *fp; + file *next; + + file (FILE *f); +}; + +/* + * file - initialize all fields to NULL + */ + +file::file (FILE *f) + : fp(f), next(0) +{ +} + +class files { +public: + files (); + FILE *get_file (void); + void start_of_list (void); + void move_next (void); + void add_new_file (FILE *f); +private: + file *head; + file *tail; + file *ptr; +}; + +/* + * files - create an empty list of files. + */ + +files::files () + : head(0), tail(0), ptr(0) +{ +} + +/* + * get_file - returns the FILE associated with ptr. + */ + +FILE *files::get_file (void) +{ + if (ptr) { + return( ptr->fp ); + } else { + return( 0 ); + } +} + +/* + * start_of_list - reset the ptr to the start of the list. + */ + +void files::start_of_list (void) +{ + ptr = head; +} + +/* + * move_next - moves the ptr to the next element on the list. + */ + +void files::move_next (void) +{ + if (ptr != 0) + ptr = ptr->next; +} + +/* + * add_new_file - adds a new file, f, to the list. + */ + +void files::add_new_file (FILE *f) +{ + if (head == 0) { + head = new file(f); + tail = head; + } else { + tail->next = new file(f); + tail = tail->next; + } + ptr = tail; +} + +/* + * the class and methods for styles + */ + +struct style { + font *f; + int point_size; + int font_no; + int height; + int slant; + style (); + style (font *, int, int, int, int); + int operator == (const style &) const; + int operator != (const style &) const; +}; + +style::style() + : f(0) +{ +} + +style::style(font *p, int sz, int h, int sl, int no) + : f(p), point_size(sz), font_no(no), height(h), slant(sl) +{ +} + +int style::operator==(const style &s) const +{ + return (f == s.f && point_size == s.point_size + && height == s.height && slant == s.slant); +} + +int style::operator!=(const style &s) const +{ + return !(*this == s); +} + +/* + * the class and methods for retaining ascii text + */ + +struct char_block { + enum { SIZE = 256 }; + char buffer[SIZE]; + int used; + char_block *next; + + char_block(); +}; + +char_block::char_block() +: used(0), next(0) +{ +} + +class char_buffer { +public: + char_buffer(); + ~char_buffer(); + char *add_string(char *, unsigned int); +private: + char_block *head; + char_block *tail; +}; + +char_buffer::char_buffer() +: head(0), tail(0) +{ +} + +char_buffer::~char_buffer() +{ + while (head != 0) { + char_block *temp = head; + head = head->next; + delete temp; + } +} + +char *char_buffer::add_string (char *s, unsigned int length) +{ + int i=0; + unsigned int old_used; + + if (tail == 0) { + tail = new char_block; + head = tail; + } else { + if (tail->used + length+1 > char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + } + } + // at this point we have a tail which is ready for the string. + if (tail->used + length+1 > char_block::SIZE) { + fatal("need to increase char_block::SIZE"); + } + + old_used = tail->used; + do { + tail->buffer[tail->used] = s[i]; + tail->used++; + i++; + length--; + } while (length>0); + + // add terminating nul character + + tail->buffer[tail->used] = '\0'; + tail->used++; + + // and return start of new string + + return( &tail->buffer[old_used] ); +} + +/* + * the classes and methods for maintaining glyph positions. + */ + +class text_glob { +public: + text_glob (style *s, char *string, unsigned int length, + int min_vertical , int min_horizontal, + int max_vertical , int max_horizontal, + int is_html , int is_troff_command, + int is_auto_image, + int is_a_line , int thickness); + text_glob (void); + ~text_glob (void); + int is_a_line (void); + int is_a_tag (void); + int is_raw (void); + int is_eol (void); + int is_auto_img (void); + int is_br (void); + + style text_style; + char *text_string; + unsigned int text_length; + int minv, maxv, minh, maxh; + int is_raw_command; // should the text be sent directly to the device? + int is_tag; // is this a .br, .sp, .tl etc + int is_line; // is the command a <line>? + int is_img_auto; // image created by eqn delim + int thickness; // the thickness of a line +}; + +text_glob::text_glob (style *s, char *string, unsigned int length, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal, + int is_html, int is_troff_command, + int is_auto_image, + int is_a_line, int line_thickness) + : text_style(*s), text_string(string), text_length(length), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal), + is_raw_command(is_html), is_tag(is_troff_command), is_img_auto(is_auto_image), + is_line(is_a_line), thickness(line_thickness) +{ +} + +text_glob::text_glob () + : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1), + is_raw_command(FALSE), is_tag(FALSE), is_line(FALSE), thickness(0) +{ +} + +text_glob::~text_glob () +{ +} + +/* + * is_a_line - returns TRUE if glob should be converted into an <hr> + */ + +int text_glob::is_a_line (void) +{ + return( is_line ); +} + +/* + * is_a_tag - returns TRUE if glob contains a troff directive. + */ + +int text_glob::is_a_tag (void) +{ + return( is_tag ); +} + +/* + * is_eol - returns TRUE if glob contains the tag eol + */ + +int text_glob::is_eol (void) +{ + return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) ); +} + +/* + * is_raw - returns TRUE if glob contains raw html. + */ + +int text_glob::is_raw (void) +{ + return( is_raw_command ); +} + +/* + * is_auto_img - returns TRUE if the glob contains an automatically + * generated image. + */ + +int text_glob::is_auto_img (void) +{ + return( is_img_auto ); +} + +/* + * is_br - returns TRUE if the glob is a tag containing a .br + */ + +int text_glob::is_br (void) +{ + return( is_a_tag() && (strcmp("html-tag:.br", text_string) == 0) ); +} + +/* + * the class and methods used to construct ordered double linked lists. + * In a previous implementation we used templates via #include "ordered-list.h", + * but this does assume that all C++ compilers can handle this feature. Pragmatically + * it is safer to assume this is not the case. + */ + +struct element_list { + element_list *right; + element_list *left; + text_glob *datum; + int lineno; + int minv, maxv, minh, maxh; + + element_list (text_glob *d, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + element_list (); +}; + +element_list::element_list () + : right(0), left(0), datum(0), lineno(0), minv(-1), maxv(-1), minh(-1), maxh(-1) +{ +} + +/* + * element_list - create a list element assigning the datum and region parameters. + */ + +element_list::element_list (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) + : right(0), left(0), datum(in), lineno(line_number), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal) +{ +} + +class list { +public: + list (); + ~list (); + int is_less (element_list *a, element_list *b); + void add (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void sub_move_right (void); + void move_right (void); + void move_left (void); + int is_empty (void); + int is_equal_to_tail (void); + int is_equal_to_head (void); + void start_from_head (void); + void start_from_tail (void); + text_glob *move_right_get_data (void); + text_glob *move_left_get_data (void); + text_glob *get_data (void); +private: + element_list *head; + element_list *tail; + element_list *ptr; +}; + +/* + * list - construct an empty list. + */ + +list::list () + : head(0), tail(0), ptr(0) +{ +} + +/* + * ~list - destroy a complete list. + */ + +list::~list() +{ + element_list *temp=head; + + do { + temp = head; + if (temp != 0) { + head = head->right; + delete temp; + } + } while ((head != 0) && (head != tail)); +} + +/* + * is_less - returns TRUE if a is left of b if on the same line or + * if a is higher up the page than b. + */ + +int list::is_less (element_list *a, element_list *b) +{ + // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) { + if (a->lineno < b->lineno) { + return( TRUE ); + } else if (a->lineno > b->lineno) { + return( FALSE ); + } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) { + return( a->minh < b->minh ); + } else { + return( a->maxv < b->maxv ); + } +} + +/* + * add - adds a datum to the list in the order specified by the region position. + */ + +void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal) +{ + // create a new list element with datum and position fields initialized + element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + element_list *last; + + if (head == 0) { + head = t; + tail = t; + t->left = t; + t->right = t; + } else { + last = tail; + + while ((last != head) && (is_less(t, last))) { + last = last->left; + } + + if (is_less(t, last)) { + t->right = last; + last->left->right = t; + t->left = last->left; + last->left = t; + // now check for a new head + if (last == head) { + head = t; + } + } else { + // add t beyond last + t->right = last->right; + t->left = last; + last->right->left = t; + last->right = t; + // now check for a new tail + if (last == tail) { + tail = t; + } + } + } +} + +/* + * sub_move_right - removes the element which is currently pointed to by ptr + * from the list and moves ptr to the right. + */ + +void list::sub_move_right (void) +{ + element_list *t=ptr->right; + + if (head == tail) { + head = 0; + if (tail != 0) { + delete tail; + } + tail = 0; + ptr = 0; + } else { + if (head == ptr) { + head = head->right; + } + if (tail == ptr) { + tail = tail->left; + } + ptr->left->right = ptr->right; + ptr->right->left = ptr->left; + ptr=t; + } +} + +/* + * start_from_head - assigns ptr to the head. + */ + +void list::start_from_head (void) +{ + ptr = head; +} + +/* + * start_from_tail - assigns ptr to the tail. + */ + +void list::start_from_tail (void) +{ + ptr = tail; +} + +/* + * is_empty - returns TRUE if the list has no elements. + */ + +int list::is_empty (void) +{ + return( head == 0 ); +} + +/* + * is_equal_to_tail - returns TRUE if the ptr equals the tail. + */ + +int list::is_equal_to_tail (void) +{ + return( ptr == tail ); +} + +/* + * is_equal_to_head - returns TRUE if the ptr equals the head. + */ + +int list::is_equal_to_head (void) +{ + return( ptr == head ); +} + +/* + * move_left - moves the ptr left. + */ + +void list::move_left (void) +{ + ptr = ptr->left; +} + +/* + * move_right - moves the ptr right. + */ + +void list::move_right (void) +{ + ptr = ptr->right; +} + +/* + * get_datum - returns the datum referenced via ptr. + */ + +text_glob* list::get_data (void) +{ + return( ptr->datum ); +} + +/* + * move_right_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_right_get_data (void) +{ + ptr = ptr->right; + if (ptr == head) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * move_left_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_left_get_data (void) +{ + ptr = ptr->left; + if (ptr == tail) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * page class and methods + */ + +class page { +public: + page (void); + void add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness); + void dump_page (void); // debugging method + + // and the data + + list glyphs; // position of glyphs and specials on page + char_buffer buffer; // all characters for this page +}; + +page::page() +{ +} + +void page::add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, FALSE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_html - add a raw html command, for example mailto, line, background, image etc. + */ + +void page::add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + TRUE, FALSE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_tag - adds a troff tag, for example: .tl .sp .br + */ + +void page::add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, TRUE, + (strncmp(string, "html-tag:.auto-image", 20) == 0), + FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_line - adds the <line> primitive providing that y1==y2 + */ + +void page::add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness) +{ + if (y1 == y2) { + text_glob *g = new text_glob(s, "", 0, + min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2), + FALSE, TRUE, FALSE, FALSE, thickness); + glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2)); + } +} + +/* + * dump_page - dump the page contents for debugging purposes. + */ + +void page::dump_page(void) +{ + text_glob *g; + + printf("\n\ndebugging start\n"); + glyphs.start_from_head(); + do { + g = glyphs.get_data(); + printf("%s ", g->text_string); + glyphs.move_right(); + } while (! glyphs.is_equal_to_head()); + printf("\ndebugging end\n\n"); +} + +/* + * font classes and methods + */ + +class html_font : public font { + html_font(const char *); +public: + int encoding_index; + char *encoding; + char *reencoded_name; + ~html_font(); + static html_font *load_html_font(const char *); +}; + +html_font *html_font::load_html_font(const char *s) +{ + html_font *f = new html_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +html_font::html_font(const char *nm) +: font(nm) +{ +} + +html_font::~html_font() +{ +} + +/* + * a simple class to contain the header to this document + */ + +class title_desc { +public: + title_desc (); + ~title_desc (); + + int has_been_written; + int has_been_found; + char text[MAX_STRING_LENGTH]; +}; + + +title_desc::title_desc () + : has_been_written(FALSE), has_been_found(FALSE) +{ +} + +title_desc::~title_desc () +{ +} + +class header_desc { +public: + header_desc (); + ~header_desc (); + + int no_of_headings; // how many headings have we found? + char_buffer headings; // all the headings used in the document + list headers; // list of headers built from .NH and .SH + int header_level; // current header level + int written_header; // have we written the header yet? + char header_buffer[MAX_STRING_LENGTH]; // current header text + + void write_headings (FILE *f, int force); +}; + +header_desc::header_desc () + : no_of_headings(0), header_level(2), written_header(0) +{ +} + +header_desc::~header_desc () +{ +} + +/* + * write_headings - emits a list of links for the headings in this document + */ + +void header_desc::write_headings (FILE *f, int force) +{ + text_glob *g; + + if (auto_links || force) { + if (! headers.is_empty()) { + int h=1; + + headers.start_from_head(); + do { + g = headers.get_data(); + fputs("<a href=\"#", f); + if (simple_anchors) + fprintf(f, ANCHOR_TEMPLATE, h); + else + fputs(g->text_string, f); + h++; + fputs("\">", f); + fputs(g->text_string, f); + fputs("</a><br>\n", f); + headers.move_right(); + } while (! headers.is_equal_to_head()); + fputs("\n", f); + } + } +} + +class html_printer : public printer { + files file_list; + simple_output html; + int res; + int space_char_index; + int no_of_printed_pages; + int paper_length; + enum { SBUF_SIZE = 8192 }; + char sbuf[SBUF_SIZE]; + int sbuf_len; + int sbuf_start_hpos; + int sbuf_vpos; + int sbuf_end_hpos; + int sbuf_kern; + style sbuf_style; + style output_style; + int output_hpos; + int output_vpos; + int output_vpos_max; + int output_draw_point_size; + int line_thickness; + int output_line_thickness; + unsigned char output_space_code; + string defs; + char *inside_font_style; + int page_number; + title_desc title; + title_desc indent; // use title class to remember $1 of .ip + header_desc header; + int header_indent; + int supress_sub_sup; + int cutoff_heading; + page *page_contents; + html_text *current_paragraph; + int end_center; + int end_tempindent; + TAG_ALIGNMENT next_tag; + int fill_on; + int linelength; + int pageoffset; + int indentation; + int prev_indent; + int pointsize; + int vertical_spacing; + int line_number; + + void flush_sbuf (); + void set_style (const style &); + void set_space_code (unsigned char c); + void do_exec (char *, const environment *); + void do_import (char *, const environment *); + void do_def (char *, const environment *); + void do_mdef (char *, const environment *); + void do_file (char *, const environment *); + void set_line_thickness (const environment *); + void terminate_current_font (void); + void flush_font (void); + void add_char_to_sbuf (unsigned char code); + void add_to_sbuf (unsigned char code, const char *name); + void write_title (int in_head); + void determine_diacritical_mark (const char *name, const environment *env); + int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w); + char *remove_last_char_from_sbuf (); + int seen_backwards_escape (char *s, int l); + void flush_page (void); + void troff_tag (text_glob *g); + void flush_globs (void); + void emit_line (text_glob *g); + void emit_raw (text_glob *g); + void translate_to_html (text_glob *g); + void determine_space (text_glob *g); + void start_font (const char *name); + void end_font (const char *name); + int is_font_courier (font *f); + int is_courier_until_eol (void); + void start_size (int from, int to); + void do_font (text_glob *g); + void do_center (char *arg); + void do_break (void); + void do_eol (void); + void do_title (void); + void do_fill (int on); + void do_heading (char *arg); + void write_header (void); + void determine_header_level (int level); + void do_linelength (char *arg); + void do_pageoffset (char *arg); + void do_indentation (char *arg); + void do_tempindent (char *arg); + void do_indentedparagraph (void); + void do_verticalspacing (char *arg); + void do_pointsize (char *arg); + void do_centered_image (void); + void do_left_image (void); + void do_right_image (void); + void do_auto_image (text_glob *g, const char *filename); + void do_links (void); + void do_flush (void); + int is_in_middle (int left, int right); + void do_sup_or_sub (text_glob *g); + int start_subscript (text_glob *g); + int end_subscript (text_glob *g); + int start_superscript (text_glob *g); + int end_superscript (text_glob *g); + + // ADD HERE + +public: + html_printer (); + ~html_printer (); + void set_char (int i, font *f, const environment *env, int w, const char *name); + void draw (int code, int *p, int np, const environment *env); + void begin_page (int); + void end_page (int); + void special (char *arg, const environment *env, char type); + font *make_font (const char *); + void end_of_line (); +}; + +printer *make_printer() +{ + return new html_printer; +} + +static void usage(FILE *stream); + +void html_printer::set_style(const style &sty) +{ + const char *fontname = sty.f->get_name(); + if (fontname == 0) + fatal("no internalname specified for font"); + +#if 0 + change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size); +#endif +} + +void html_printer::end_of_line() +{ + flush_sbuf(); + line_number++; +} + +/* + * emit_line - writes out a horizontal rule. + */ + +void html_printer::emit_line (text_glob *g) +{ + // --fixme-- needs to know the length in percentage + html.put_string("<hr>"); +} + +/* + * emit_raw - writes the raw html information directly to the device. + */ + +void html_printer::emit_raw (text_glob *g) +{ + do_font(g); + if (next_tag == INLINE) { + determine_space(g); + current_paragraph->do_emittext(g->text_string, g->text_length); + } else { + int in_table=current_paragraph->is_in_table(); + + current_paragraph->done_para(); + switch (next_tag) { + + case CENTERED: + current_paragraph->do_para("align=center"); + break; + case LEFT: + current_paragraph->do_para("align=left"); + break; + case RIGHT: + current_paragraph->do_para("align=right"); + break; + default: + fatal("unknown enumeration"); + } + current_paragraph->do_emittext(g->text_string, g->text_length); + current_paragraph->done_para(); + next_tag = INLINE; + supress_sub_sup = TRUE; +#if defined(INDENTATION) + if (in_table) { + stop(); + current_paragraph->do_indent(NULL, 0, pageoffset, linelength); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif + } +} + +/* + * do_center - handle the .ce commands from troff. + */ + +void html_printer::do_center (char *arg) +{ + int n = atoi(arg); + + current_paragraph->do_break(); + current_paragraph->done_para(); + supress_sub_sup = TRUE; + + if (n > 0) { + current_paragraph->do_para("align=center"); + end_center += n; + } else { + end_center = 0; + } +} + +/* + * do_centered_image - set a flag such that the next html-tag is + * placed inside a centered paragraph. + */ + +void html_printer::do_centered_image (void) +{ + next_tag = CENTERED; +} + +/* + * do_right_image - set a flag such that the next html-tag is + * placed inside a right aligned paragraph. + */ + +void html_printer::do_right_image (void) +{ + next_tag = RIGHT; +} + +/* + * do_left_image - set a flag such that the next html-tag is + * placed inside a left aligned paragraph. + */ + +void html_printer::do_left_image (void) +{ + next_tag = LEFT; +} + +/* + * exists - returns TRUE if filename exists. + */ + +static int exists (const char *filename) +{ + FILE *fp = fopen(filename, "r"); + + if (fp == 0) { + return( FALSE ); + } else { + fclose(fp); + return( TRUE ); + } +} + +/* + * generate_img_src - returns a html image tag for the filename + * providing that the image exists. + */ + +static char *generate_img_src (const char *filename) +{ + static char buffer[MAX_STRING_LENGTH]; + + while (filename && (filename[0] == ' ')) { + filename++; + } + if (exists(filename)) { + strcpy(buffer, "<img src=\""); + strncat(buffer, filename, MAX_STRING_LENGTH-strlen("<img src=\"")-1); + if (strlen(buffer) < MAX_STRING_LENGTH-3) { + strncat(buffer, "\">", 3); + } + return( (char *)&buffer ); + } else { + return( 0 ); + } +} + +/* + * do_auto_image - tests whether the image, indicated by filename, + * is present, if so then it emits an html image tag. + * An image tag may be passed through from pic, eqn + * but the corresponding image might not be created. + * Consider .EQ delim $$ .EN or an empty .PS .PE. + */ + +void html_printer::do_auto_image (text_glob *g, const char *filename) +{ + char *buffer = generate_img_src(filename); + + if (buffer) { + /* + * utilize emit_raw by creating a new text_glob. + */ + text_glob h = *g; + + h.text_string = buffer; + h.text_length = strlen(buffer); + emit_raw(&h); + } else { + next_tag = INLINE; + } +} + +/* + * do_title - handle the .tl commands from troff. + */ + +void html_printer::do_title (void) +{ + text_glob *t; + int removed_from_head; + char buf[MAX_STRING_LENGTH]; + + if (page_number == 1) { + int found_title_start = FALSE; + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + do { + t = page_contents->glyphs.get_data(); + removed_from_head = FALSE; + if (t->is_auto_img()) { + char *img=generate_img_src((char *)(t->text_string + 20)); + + if (img) { + if (found_title_start) { + strcat(title.text, " "); + } + found_title_start = TRUE; + title.has_been_found = TRUE; + strcat(title.text, img); + } + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else if (t->is_raw_command) { + /* skip raw commands + */ + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_eol()) { + /* end of title found + */ + title.has_been_found = TRUE; + return; + } else if (t->is_a_tag()) { + /* end of title found, but move back so that we read this tag and process it + */ + page_contents->glyphs.move_left(); /* move backwards to last word */ + title.has_been_found = TRUE; + return; + } else if (found_title_start) { + strcat(title.text, " "); + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcat(title.text, buf); + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else { + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcpy((char *)title.text, buf); + found_title_start = TRUE; + title.has_been_found = TRUE; + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } + } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head)); + } + // page_contents->glyphs.move_left(); /* move backwards to last word */ + } +} + +void html_printer::write_header (void) +{ + if (strlen(header.header_buffer) > 0) { + if (header.header_level > 7) { + header.header_level = 7; + } + + // firstly we must terminate any font and type faces + current_paragraph->done_para(); + current_paragraph->done_table(); + supress_sub_sup = TRUE; + + if (cutoff_heading+2 > header.header_level) { + // now we save the header so we can issue a list of links + header.no_of_headings++; + style st; + + text_glob *h=new text_glob(&st, + header.headings.add_string(header.header_buffer, strlen(header.header_buffer)), + strlen(header.header_buffer), + header.no_of_headings, header.header_level, + header.no_of_headings, header.header_level, + FALSE, FALSE, FALSE, FALSE, FALSE); + header.headers.add(h, + header.no_of_headings, + header.no_of_headings, header.no_of_headings, + header.no_of_headings, header.no_of_headings); // and add this header to the header list + + // lastly we generate a tag + + html.nl().put_string("<a name=\""); + if (simple_anchors) { + char buffer[MAX_LINE_LENGTH]; + + sprintf(buffer, ANCHOR_TEMPLATE, header.no_of_headings); + html.put_string(buffer); + } else { + html.put_string(header.header_buffer); + } + html.put_string("\"></a>").nl(); + } + + // and now we issue the real header + html.put_string("<h"); + html.put_number(header.header_level); + html.put_string(">"); + html.put_string(header.header_buffer); + html.put_string("</h"); + html.put_number(header.header_level); + html.put_string(">").nl(); + + current_paragraph->do_para(""); + } +} + +void html_printer::determine_header_level (int level) +{ + if (level == 0) { + int i; + int l=strlen(header.header_buffer); + + for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) { + if (header.header_buffer[i] == '.') { + level++; + } + } + } + header.header_level = level+1; +} + +/* + * do_heading - handle the .SH and .NH and equivalent commands from troff. + */ + +void html_printer::do_heading (char *arg) +{ + text_glob *g; + text_glob *l = 0; + char buf[MAX_STRING_LENGTH]; + int level=atoi(arg); + + strcpy(header.header_buffer, ""); + page_contents->glyphs.move_right(); + if (! page_contents->glyphs.is_equal_to_head()) { + g = page_contents->glyphs.get_data(); + do { + if (g->is_auto_img()) { + char *img=generate_img_src((char *)(g->text_string + 20)); + + if (img) { + simple_anchors = TRUE; // we cannot use full heading anchors with images + if (l != 0) { + strcat(header.header_buffer, " "); + } + l = g; + strcat(header.header_buffer, img); + } + } else if (! (g->is_a_line() || g->is_a_tag() || g->is_raw())) { + /* + * we ignore raw commands when constructing a heading + */ + if (l != 0) { + strcat(header.header_buffer, " "); + } + l = g; + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE); + strcat(header.header_buffer, (char *)buf); + } + page_contents->glyphs.move_right(); + g = page_contents->glyphs.get_data(); + } while ((! page_contents->glyphs.is_equal_to_head()) && + (! g->is_br())); + } + + determine_header_level(level); + write_header(); + + // finally set the output to neutral for after the header + g = page_contents->glyphs.get_data(); + page_contents->glyphs.move_left(); // so that next time we use old g +} + +/* + * is_courier_until_eol - returns TRUE if we can see a whole line which is courier + */ + +int html_printer::is_courier_until_eol (void) +{ + text_glob *orig = page_contents->glyphs.get_data(); + int result = TRUE; + text_glob *g; + + if (! page_contents->glyphs.is_equal_to_tail()) { + page_contents->glyphs.move_right(); + do { + g = page_contents->glyphs.get_data(); + if (! is_font_courier(g->text_style.f)) { + result = FALSE; + } + page_contents->glyphs.move_right(); + } while ((result) && + (! page_contents->glyphs.is_equal_to_head()) && + (! g->is_eol())); + + /* + * now restore our previous position. + */ + while (page_contents->glyphs.get_data() != orig) { + page_contents->glyphs.move_left(); + } + } + return( result ); +} + +/* + * do_linelength - handle the .ll command from troff. + */ + +void html_printer::do_linelength (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + linelength = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_pageoffset - handle the .po command from troff. + */ + +void html_printer::do_pageoffset (char *arg) +{ +#if defined(INDENTATION) + pageoffset = atoi(arg); + if (fill_on) { + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_indentation - handle the .in command from troff. + */ + +void html_printer::do_indentation (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + indentation = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_tempindent - handle the .ti command from troff. + */ + +void html_printer::do_tempindent (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + end_tempindent = 1; + prev_indent = indentation; + indentation = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_indentedparagraph - handle the .ip tag, this buffers the next line + * and passes this to text-text as the left hand + * column table entry. + */ + +void html_printer::do_indentedparagraph (void) +{ +#if defined(INDENTATION) + text_glob *t; + int removed_from_head; + char buf[MAX_STRING_LENGTH]; + int found_indent_start = FALSE; + + indent.has_been_found = FALSE; + indent.text[0] = (char)0; + + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + do { + t = page_contents->glyphs.get_data(); + removed_from_head = FALSE; + if (t->is_auto_img()) { + char *img=generate_img_src((char *)(t->text_string + 20)); + + if (img) { + if (found_indent_start) { + strcat(indent.text, " "); + } + found_indent_start = TRUE; + strcat(indent.text, img); + } + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_raw_command) { + /* skip raw commands + */ + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_a_tag() && (strncmp(t->text_string, "html-tag:.br", 12) == 0)) { + /* end of indented para found, but move back so that we read this tag and process it + */ + page_contents->glyphs.move_left(); /* move backwards to last word */ + indent.has_been_found = TRUE; + return; + } else if (t->is_a_tag()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (found_indent_start) { + strcat(indent.text, " "); + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcat(indent.text, buf); + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else { + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcpy((char *)indent.text, buf); + found_indent_start = TRUE; + indent.has_been_found = TRUE; + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } + } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head)); + } + // page_contents->glyphs.move_left(); /* move backwards to last word */ +#endif +} + +/* + * do_verticalspacing - handle the .vs command from troff. + */ + +void html_printer::do_verticalspacing (char *arg) +{ + vertical_spacing = atoi(arg); +} + +/* + * do_pointsize - handle the .ps command from troff. + */ + +void html_printer::do_pointsize (char *arg) +{ + pointsize = atoi(arg); +} + +/* + * do_fill - records whether troff has requested that text be filled. + */ + +void html_printer::do_fill (int on) +{ + current_paragraph->do_break(); + output_hpos = indentation+pageoffset; + supress_sub_sup = TRUE; + + if (fill_on != on) { + if (on) { + current_paragraph->done_pre(); + } else { + current_paragraph->do_pre(); + } + } + fill_on = on; +} + +/* + * do_eol - handle the end of line + */ + +void html_printer::do_eol (void) +{ + if (! fill_on) { + current_paragraph->do_newline(); + current_paragraph->do_break(); + } + output_hpos = indentation+pageoffset; + if (end_center > 0) { + if (end_center > 1) { + current_paragraph->do_break(); + } + end_center--; + if (end_center == 0) { + current_paragraph->done_para(); + supress_sub_sup = TRUE; + } + } +} + +/* + * do_flush - flushes all output and tags. + */ + +void html_printer::do_flush (void) +{ + current_paragraph->done_para(); + current_paragraph->done_table(); +} + +/* + * do_links - moves onto a new temporary file and sets auto_links to FALSE. + */ + +void html_printer::do_links (void) +{ + current_paragraph->done_para(); + current_paragraph->done_table(); + auto_links = FALSE; /* from now on only emit under user request */ +#if !defined(DEBUGGING) + file_list.add_new_file(xtmpfile()); + html.set_file(file_list.get_file()); +#endif +} + +/* + * do_break - handles the ".br" request and also + * undoes an outstanding ".ti" command. + */ + +void html_printer::do_break (void) +{ + current_paragraph->do_break(); +#if defined(INDENTATION) + if (end_tempindent > 0) { + end_tempindent--; + if (end_tempindent == 0) { + indentation = prev_indent; + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } + } +#endif + output_hpos = indentation+pageoffset; + supress_sub_sup = TRUE; +} + +/* + * troff_tag - processes the troff tag and manipulates the troff state machine. + */ + +void html_printer::troff_tag (text_glob *g) +{ + /* + * firstly skip over html-tag: + */ + char *t=(char *)g->text_string+9; + + if (g->is_eol()) { + do_eol(); + } else if (strncmp(t, ".sp", 3) == 0) { + current_paragraph->do_space(); + supress_sub_sup = TRUE; + } else if (strncmp(t, ".br", 3) == 0) { + do_break(); + } else if (strcmp(t, ".centered-image") == 0) { + do_centered_image(); + } else if (strcmp(t, ".right-image") == 0) { + do_right_image(); + } else if (strcmp(t, ".left-image") == 0) { + do_left_image(); + } else if (strncmp(t, ".auto-image", 11) == 0) { + char *a = (char *)t+11; + do_auto_image(g, a); + } else if (strncmp(t, ".ce", 3) == 0) { + char *a = (char *)t+3; + supress_sub_sup = TRUE; + do_center(a); + } else if (strncmp(t, ".tl", 3) == 0) { + supress_sub_sup = TRUE; + do_title(); + } else if (strncmp(t, ".fi", 3) == 0) { + do_fill(TRUE); + } else if (strncmp(t, ".nf", 3) == 0) { + do_fill(FALSE); + } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) { + char *a = (char *)t+3; + do_heading(a); + } else if (strncmp(t, ".ll", 3) == 0) { + char *a = (char *)t+3; + do_linelength(a); + } else if (strncmp(t, ".po", 3) == 0) { + char *a = (char *)t+3; + do_pageoffset(a); + } else if (strncmp(t, ".in", 3) == 0) { + char *a = (char *)t+3; + do_indentation(a); + } else if (strncmp(t, ".ti", 3) == 0) { + char *a = (char *)t+3; + do_tempindent(a); + } else if (strncmp(t, ".vs", 3) == 0) { + char *a = (char *)t+3; + do_verticalspacing(a); + } else if (strncmp(t, ".ip", 3) == 0) { + do_indentedparagraph(); + } else if (strcmp(t, ".links") == 0) { + do_links(); + } +} + +/* + * is_in_middle - returns TRUE if the positions left..right are in the center of the page. + */ + +int html_printer::is_in_middle (int left, int right) +{ + return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE ); +} + +/* + * flush_globs - runs through the text glob list and emits html. + */ + +void html_printer::flush_globs (void) +{ + text_glob *g; + + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.start_from_head(); + do { + g = page_contents->glyphs.get_data(); + + if (strcmp(g->text_string, "XXXXXXX") == 0) { + stop(); + } + + if (g->is_raw()) { + emit_raw(g); + } else if (g->is_a_tag()) { + troff_tag(g); + } else if (g->is_a_line()) { + emit_line(g); + } else { + translate_to_html(g); + } + /* + * after processing the title (and removing it) the glyph list might be empty + */ + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.move_right(); + } + } while (! page_contents->glyphs.is_equal_to_head()); + } +} + +void html_printer::flush_page (void) +{ + supress_sub_sup = TRUE; + flush_sbuf(); + // page_contents->dump_page(); + flush_globs(); + current_paragraph->done_para(); + current_paragraph->done_table(); + + // move onto a new page + delete page_contents; + page_contents = new page; +} + +/* + * determine_space - works out whether we need to write a space. + * If last glyth is ajoining then no space emitted. + */ + +void html_printer::determine_space (text_glob *g) +{ + if (current_paragraph->is_in_pre()) { + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + /* + * .nf has been specified + */ + while (output_hpos < g->minh) { + output_hpos += space_width; + current_paragraph->emit_space(); + } + } else { + if ((output_vpos != g->minv) || (output_hpos < g->minh)) { + current_paragraph->emit_space(); + } + } +} + +/* + * is_font_courier - returns TRUE if the font, f, is courier. + */ + +int html_printer::is_font_courier (font *f) +{ + if (f != 0) { + const char *fontname = f->get_name(); + + return( (fontname != 0) && (fontname[0] == 'C') ); + } + return( FALSE ); +} + +/* + * end_font - shuts down the font corresponding to fontname. + */ + +void html_printer::end_font (const char *fontname) +{ + if (strcmp(fontname, "B") == 0) { + current_paragraph->done_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->done_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + } else if (strcmp(fontname, "CR") == 0) { + current_paragraph->done_tt(); + current_paragraph->done_pre(); + } +} + +/* + * start_font - starts the font corresponding to name. + */ + +void html_printer::start_font (const char *fontname) +{ + if (strcmp(fontname, "R") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + current_paragraph->done_tt(); + } else if (strcmp(fontname, "B") == 0) { + current_paragraph->do_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->do_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->do_bold(); + current_paragraph->do_italic(); + } else if (strcmp(fontname, "CR") == 0) { + if ((! fill_on) && (is_courier_until_eol())) { + current_paragraph->do_pre(); + } + current_paragraph->do_tt(); + } +} + +/* + * start_size - from is old font size, to is the new font size. + * The html increase <big> and <small> decrease alters the + * font size by 20%. We try and map these onto glyph sizes. + */ + +void html_printer::start_size (int from, int to) +{ + if (from < to) { + while (from < to) { + current_paragraph->do_big(); + from += SIZE_INCREMENT; + } + } else if (from > to) { + while (from > to) { + current_paragraph->do_small(); + from -= SIZE_INCREMENT; + } + } +} + +/* + * do_font - checks to see whether we need to alter the html font. + */ + +void html_printer::do_font (text_glob *g) +{ + /* + * check if the output_style.point_size has not been set yet + * this allow users to place .ps at the top of their troff files + * and grohtml can then treat the .ps value as the base font size (3) + */ + if (output_style.point_size == -1) { + output_style.point_size = pointsize; + } + + if (g->text_style.f != output_style.f) { + if (output_style.f != 0) { + end_font(output_style.f->get_name()); + } + output_style.f = g->text_style.f; + if (output_style.f != 0) { + start_font(output_style.f->get_name()); + } + } + if (output_style.point_size != g->text_style.point_size) { + do_sup_or_sub(g); + if ((output_style.point_size > 0) && + (g->text_style.point_size > 0)) { + start_size(output_style.point_size, g->text_style.point_size); + } + if (g->text_style.point_size > 0) { + output_style.point_size = g->text_style.point_size; + } + } +} + +/* + * start_subscript - returns TRUE if, g, looks like a subscript start. + */ + +int html_printer::start_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos < g->minv) && + (output_vpos-height > g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * start_superscript - returns TRUE if, g, looks like a superscript start. + */ + +int html_printer::start_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos > g->minv) && + (output_vpos-height < g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * end_subscript - returns TRUE if, g, looks like the end of a subscript. + */ + +int html_printer::end_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv < output_vpos) && + (output_vpos-height > g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * end_superscript - returns TRUE if, g, looks like the end of a superscript. + */ + +int html_printer::end_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv > output_vpos) && + (output_vpos-height < g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript + * start/end and it calls the services of html-text to issue the + * appropriate tags. + */ + +void html_printer::do_sup_or_sub (text_glob *g) +{ + if (! supress_sub_sup) { + if (start_subscript(g)) { + current_paragraph->do_sub(); + } else if (start_superscript(g)) { + current_paragraph->do_sup(); + } else if (end_subscript(g)) { + current_paragraph->done_sub(); + } else if (end_superscript(g)) { + current_paragraph->done_sup(); + } + } +} + +/* + * translate_to_html - translates a textual string into html text + */ + +void html_printer::translate_to_html (text_glob *g) +{ + char buf[MAX_STRING_LENGTH]; + + do_font(g); + determine_space(g); + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, + g->text_string, g->text_length, TRUE); + current_paragraph->do_emittext(buf, strlen(buf)); + output_vpos = g->minv; + output_hpos = g->maxh; + output_vpos_max = g->maxv; + supress_sub_sup = FALSE; +} + +/* + * flush_sbuf - flushes the current sbuf into the list of glyphs. + */ + +void html_printer::flush_sbuf() +{ + if (sbuf_len > 0) { + int r=font::res; // resolution of the device + set_style(sbuf_style); + + page_contents->add(&sbuf_style, sbuf, sbuf_len, + line_number, + sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos, + sbuf_vpos , sbuf_end_hpos); + + output_hpos = sbuf_end_hpos; + output_vpos = sbuf_vpos; + sbuf_len = 0; + } +} + +void html_printer::set_line_thickness(const environment *env) +{ + line_thickness = env->size; +} + +void html_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + + case 'l': + if (np == 2) { + page_contents->add_line(&sbuf_style, + line_number, + env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness); + } else { + error("2 arguments required for line"); + } + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + + case 'P': + // fall through + case 'p': + { +#if 0 + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + // firstly lets add our current position to polygon + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (i<np) { + p[i+0] += oh; + p[i+1] += ov; + oh = p[i+0]; + ov = p[i+1]; + i += 2; + } + // now store polygon in page + page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill); +#endif + } + break; + case 'E': + // fall through + case 'e': +#if 0 + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2, + env->size, fill); +#endif + break; + case 'C': + // fill circle + + case 'c': + { +#if 0 + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2, + env->size, fill); +#endif + } + break; + case 'a': + { +#if 0 + if (np == 4) { + double c[2]; + + if (adjust_arc_center(p, c)) { + page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill); + } else { + // a straignt line + page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill); + } + } else { + error("4 arguments required for arc"); + } +#endif + } + break; + case '~': + { +#if 0 + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + // firstly lets add our current position to spline + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (i<np) { + p[i+0] += oh; + p[i+1] += ov; + oh = p[i+0]; + ov = p[i+1]; + i += 2; + } + page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill); +#endif + } + break; + case 'f': + { +#if 0 + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) { + // This means fill with the current color. + fill = FILL_MAX + 1; + } +#endif + break; + } + + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } +} + +html_printer::html_printer() +: html(0, MAX_LINE_LENGTH), + no_of_printed_pages(0), + sbuf_len(0), + output_hpos(-1), + output_vpos(-1), + output_vpos_max(-1), + line_thickness(-1), + inside_font_style(0), + page_number(0), + header_indent(-1), + supress_sub_sup(TRUE), + cutoff_heading(100), + end_center(0), + end_tempindent(0), + next_tag(INLINE), + fill_on(TRUE), + pageoffset(0), + indentation(0), + prev_indent(0), + linelength(0), + line_number(0) +{ +#if defined(DEBUGGING) + file_list.add_new_file(stdout); +#else + file_list.add_new_file(xtmpfile()); +#endif + html.set_file(file_list.get_file()); + if (font::hor != 24) + fatal("horizontal resolution must be 24"); + if (font::vert != 40) + fatal("vertical resolution must be 40"); +#if 0 + // should be sorted html.. + if (font::res % (font::sizescale*72) != 0) + fatal("res must be a multiple of 72*sizescale"); +#endif + int r = font::res; + int point = 0; + while (r % 10 == 0) { + r /= 10; + point++; + } + res = r; + html.set_fixed_point(point); + space_char_index = font::name_to_index("space"); + paper_length = font::paperlength; + linelength = font::res*13/2; + if (paper_length == 0) + paper_length = 11*font::res; + + page_contents = new page(); +} + +/* + * add_char_to_sbuf - adds a single character to the sbuf. + */ + +void html_printer::add_char_to_sbuf (unsigned char code) +{ + if (sbuf_len < SBUF_SIZE) { + sbuf[sbuf_len] = code; + sbuf_len++; + } else { + fatal("need to increase SBUF_SIZE"); + } +} + +/* + * add_to_sbuf - adds character code or name to the sbuf. + */ + +void html_printer::add_to_sbuf (unsigned char code, const char *name) +{ + if (name == 0) { + add_char_to_sbuf(code); + } else { + if (sbuf_style.f != NULL) { + char *html_glyph = get_html_translation(sbuf_style.f, name); + + if (html_glyph == NULL) { + add_char_to_sbuf(code); + } else { + int l = strlen(html_glyph); + int i; + + // Escape the name, so that "&" doesn't get expanded to "&" + // later during translate_to_html. + add_char_to_sbuf('\\'); add_char_to_sbuf('('); + + for (i=0; i<l; i++) { + add_char_to_sbuf(html_glyph[i]); + } + add_char_to_sbuf('\\'); add_char_to_sbuf(')'); + } + } + } +} + +int html_printer::sbuf_continuation (unsigned char code, const char *name, + const environment *env, int w) +{ + if (sbuf_end_hpos == env->hpos) { + add_to_sbuf(code, name); + sbuf_end_hpos += w + sbuf_kern; + return( TRUE ); + } else { + if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) && + ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) { + /* + * lets see whether a space is needed or not + */ + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + + if (env->hpos-sbuf_end_hpos < space_width/2) { + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + return( TRUE ); + } + } + } + return( FALSE ); +} + +/* + * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s + */ + +int html_printer::seen_backwards_escape (char *s, int l) +{ + /* + * this is tricky so it is broken into components for clarity + * (we let the compiler put in all back into a complex expression) + */ + if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) { + /* + * ok seen '\(' but we must now check for '\\(' + */ + if ((l>1) && (sbuf[l-2] == '\\')) { + /* + * escaped the escape + */ + return( FALSE ); + } else { + return( TRUE ); + } + } else { + return( FALSE ); + } +} + +/* + * reverse - return reversed string. + */ + +char *reverse (char *s) +{ + int i=0; + int j=strlen(s)-1; + char t; + + while (i<j) { + t = s[i]; + s[i] = s[j]; + s[j] = t; + i++; + j--; + } + return( s ); +} + +/* + * remove_last_char_from_sbuf - removes the last character from sbuf. + */ + +char *html_printer::remove_last_char_from_sbuf () +{ + int l=sbuf_len; + static char last[MAX_STRING_LENGTH]; + + if (l>0) { + l--; + if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) { + /* + * found terminating escape + */ + int i=0; + + l -= 2; + while ((l>0) && (! seen_backwards_escape(sbuf, l))) { + if (sbuf[l] == '\\') { + if (sbuf[l-1] == '\\') { + last[i] = sbuf[l]; + i++; + l--; + } + } else { + last[i] = sbuf[l]; + i++; + } + l--; + } + last[i] = (char)0; + sbuf_len = l; + if (seen_backwards_escape(sbuf, l)) { + sbuf_len--; + } + return( reverse(last) ); + } else { + if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) { + l -= 2; + sbuf_len = l; + return( "\\" ); + } else { + sbuf_len--; + last[0] = sbuf[sbuf_len]; + last[1] = (char)0; + return( last ); + } + } + } else { + return( NULL ); + } +} + +/* + * get_html_translation - given the position of the character and its name + * return the device encoding for such character. + */ + +char *get_html_translation (font *f, const char *name) +{ + int index; + + if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) { + return( NULL ); + } else { + index = f->name_to_index((char *)name); + if (index == 0) { + error("character `%s' not found", name); + return( NULL ); + } else { + if (f->contains(index)) { + return( (char *)f->get_special_device_encoding(index) ); + } else { + return( NULL ); + } + } + } +} + +/* + * to_unicode - returns a unicode translation of char, ch. + */ + +static char *to_unicode (unsigned char ch) +{ + static char buf[20]; + + sprintf(buf, "&#%u;", (unsigned int)ch); + return( buf ); +} + +/* + * char_translate_to_html - convert a single non escaped character + * into the appropriate html character. + */ + +int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single) +{ + if (and_single) { + int t, l; + char *translation; + char name[2]; + + name[0] = ch; + name[1] = (char)0; + translation = get_html_translation(f, name); + if ((translation == NULL) && (ch >= UNICODE_DESC_START)) { + translation = to_unicode(ch); + } + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + if (b<buflen) { + buf[b] = ch; + b++; + } + } + } else { + /* + * do not attempt to encode single characters + */ + if (b<buflen) { + buf[b] = ch; + b++; + } + } + return( b ); +} + +/* + * str_translate_to_html - converts a string, str, into html text. It places + * the output input buffer, buf. It truncates string, str, if + * there is not enough space in buf. + * It looks up the html character encoding of single characters + * if, and_single, is TRUE. Characters such as < > & etc. + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single) +{ + char *translation; + int e; + char escaped_char[MAX_STRING_LENGTH]; + int l; + int i=0; + int b=0; + int t=0; + +#if 0 + if (strcmp(str, "``@,;:\\\\()[]''") == 0) { + stop(); + } +#endif + while (str[i] != (char)0) { + if ((str[i]=='\\') && (i+1<len)) { + i++; // skip the leading backslash + if (str[i] == '(') { + // start of escape + i++; + e = 0; + while ((str[i] != (char)0) && + (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) { + if (str[i] == '\\') { + i++; + } + escaped_char[e] = str[i]; + e++; + i++; + } + if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) { + i += 2; + } + escaped_char[e] = (char)0; + if (e > 0) { + translation = get_html_translation(f, escaped_char); + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + int index=f->name_to_index(escaped_char); + + if (f->contains(index) && (index != 0)) { + buf[b] = f->get_code(index); + b++; + } + } + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } + buf[min(b, buflen)] = (char)0; +} + +/* + * set_char - adds a character into the sbuf if it is a continuation with the previous + * word otherwise flush the current sbuf and add character anew. + */ + +void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name) +{ + unsigned char code = f->get_code(i); + +#if 0 + if (code == ' ') { + stop(); + } +#endif + style sty(f, env->size, env->height, env->slant, env->fontno); + if (sty.slant != 0) { + if (sty.slant > 80 || sty.slant < -80) { + error("silly slant `%1' degrees", sty.slant); + sty.slant = 0; + } + } + if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) && + (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) { + return; + } else { + flush_sbuf(); + sbuf_len = 0; + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + sbuf_start_hpos = env->hpos; + sbuf_vpos = env->vpos; + sbuf_style = sty; + sbuf_kern = 0; + } +} + +/* + * write_title - writes the title to this document + */ + +void html_printer::write_title (int in_head) +{ + if (title.has_been_found) { + if (in_head) { + html.put_string("<title>"); + html.put_string(title.text); + html.put_string("</title>").nl().nl(); + } else { + title.has_been_written = TRUE; + html.put_string("<h1 align=center>"); + html.put_string(title.text); + html.put_string("</h1>").nl().nl(); + } + } else if (in_head) { + // place empty title tags to help conform to `tidy' + html.put_string("<title></title>").nl(); + } +} + +/* + * write_rule - emits a html rule tag, if the auto_rule boolean is true. + */ + +static void write_rule (void) +{ + if (auto_rule) + fputs("<hr>\n", stdout); +} + +void html_printer::begin_page(int n) +{ + page_number = n; +#if defined(DEBUGGING) + html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();; +#endif + no_of_printed_pages++; + + output_style.f = 0; + output_style.point_size= -1; + output_space_code = 32; + output_draw_point_size = -1; + output_line_thickness = -1; + output_hpos = -1; + output_vpos = -1; + output_vpos_max = -1; + current_paragraph = new html_text(&html); +#if defined(INDENTATION) + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); +#endif + current_paragraph->do_para(""); +} + +void html_printer::end_page(int) +{ + flush_sbuf(); + flush_page(); +} + +font *html_printer::make_font(const char *nm) +{ + return html_font::load_html_font(nm); +} + +html_printer::~html_printer() +{ + current_paragraph->flush_text(); + html.end_line(); + html.set_file(stdout); + /* + * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying + * the dtd, so for the moment I'll leave this commented out. + * If requested we could always emit it if a command line switch + * was present. + * + * fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\">\n", stdout); + */ + fputs("<html>\n", stdout); + fputs("<head>\n", stdout); + fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout); + fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout); + write_title(TRUE); + fputs("</head>\n", stdout); + fputs("<body>\n\n", stdout); + write_title(FALSE); + header.write_headings(stdout, FALSE); + write_rule(); + { + extern const char *Version_string; + html.begin_comment("Creator : ") + .put_string("groff ") + .put_string("version ") + .put_string(Version_string) + .end_comment(); + } + { +#ifdef LONG_FOR_TIME_T + long +#else + time_t +#endif + t = time(0); + html.begin_comment("CreationDate: ") + .put_string(ctime(&t), strlen(ctime(&t))-1) + .end_comment(); + } +#if defined(DEBUGGING) + html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment(); +#endif + html.end_line(); + html.end_line(); + /* + * now run through the file list copying each temporary file in turn and emitting the links. + */ + file_list.start_of_list(); + while (file_list.get_file() != 0) { + if (fseek(file_list.get_file(), 0L, 0) < 0) + fatal("fseek on temporary file failed"); + html.copy_file(file_list.get_file()); + fclose(file_list.get_file()); + file_list.move_next(); + if (file_list.get_file() != 0) + header.write_headings(stdout, TRUE); + } + write_rule(); + fputs("</body>\n", stdout); + fputs("</html>\n", stdout); +} + +/* + * special - handle all x X requests from troff. For post-html they allow users + * to pass raw html commands, turn auto linked headings off/on and + * also allow troff to emit tags to indicate when a: .br, .sp etc occurs. + */ + +void html_printer::special(char *s, const environment *env, char type) +{ + if (type != 'p') + return; + if (s != 0) { + flush_sbuf(); + if (env->fontno >= 0) { + style sty(get_font_from_index(env->fontno), env->size, env->height, env->slant, env->fontno); + sbuf_style = sty; + } + + if (strncmp(s, "html:", 5) == 0) { + int r=font::res; /* resolution of the device */ + char buf[MAX_STRING_LENGTH]; + font *f=sbuf_style.f; + + if (f == NULL) { + int found=FALSE; + + f = font::load_font("TR", &found); + } + str_translate_to_html(f, buf, MAX_STRING_LENGTH, + &s[5], strlen(s)-5, FALSE); + + /* + * need to pass rest of string through to html output during flush + */ + page_contents->add_html(&sbuf_style, buf, strlen(buf), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + + /* + * assume that the html command has no width, if it does then hopefully troff + * will have fudged this in a macro by requesting that the formatting move right by + * the appropriate width. + */ + } else if (strncmp(s, "index:", 6) == 0) { + cutoff_heading = atoi(&s[6]); + } else if (strncmp(s, "html-tag:", 9) == 0) { + int r=font::res; /* resolution of the device */ + + page_contents->add_tag(&sbuf_style, s, strlen(s), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + } + } +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "o:i:F:vd?lrn", long_options, NULL)) + != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + printf("GNU post-grohtml (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'F': + font::command_line_font_dir(optarg); + break; + case 'l': + auto_links = FALSE; + break; + case 'r': + auto_rule = FALSE; + break; + case 'o': + /* handled by pre-html */ + break; + case 'i': + /* handled by pre-html */ + break; + case 'n': + simple_anchors = TRUE; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + if (optind >= argc) { + do_file("-"); + } else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [-vld?n] [-F dir] [files ...]\n", + program_name); +} diff --git a/contrib/groff/src/devices/grolbp/Makefile.sub b/contrib/groff/src/devices/grolbp/Makefile.sub new file mode 100644 index 0000000..d60008b --- /dev/null +++ b/contrib/groff/src/devices/grolbp/Makefile.sub @@ -0,0 +1,6 @@ +PROG=grolbp +MAN1=grolbp.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=lbp.o +CCSRCS=$(srcdir)/lbp.cc diff --git a/contrib/groff/src/devices/grolbp/charset.h b/contrib/groff/src/devices/grolbp/charset.h new file mode 100644 index 0000000..adc76f4 --- /dev/null +++ b/contrib/groff/src/devices/grolbp/charset.h @@ -0,0 +1,69 @@ +// Definition of the WP54 character set + +char symset[] = { +0x57,0x50,0x35,0x34,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61, +0x72,0x64,0x65,0x2d,0x42,0x6f,0x6f,0x6b,0x00,0x41,0x76, +0x61,0x6e,0x74,0x47,0x61,0x72,0x64,0x65,0x2d,0x44,0x65, +0x6d,0x69,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61,0x72, +0x64,0x65,0x2d,0x42,0x6f,0x6f,0x6b,0x4f,0x62,0x6c,0x69, +0x71,0x75,0x65,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61, +0x72,0x64,0x65,0x2d,0x44,0x65,0x6d,0x69,0x4f,0x62,0x6c, +0x69,0x71,0x75,0x65,0x00,0x42,0x6f,0x6f,0x6b,0x6d,0x61, +0x6e,0x2d,0x4c,0x69,0x67,0x68,0x74,0x00,0x42,0x6f,0x6f, +0x6b,0x6d,0x61,0x6e,0x2d,0x44,0x65,0x6d,0x69,0x00,0x42, +0x6f,0x6f,0x6b,0x6d,0x61,0x6e,0x2d,0x4c,0x69,0x67,0x68, +0x74,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x42,0x6f,0x6f, +0x6b,0x6d,0x61,0x6e,0x2d,0x44,0x65,0x6d,0x69,0x49,0x74, +0x61,0x6c,0x69,0x63,0x00,0x43,0x65,0x6e,0x74,0x75,0x72, +0x79,0x53,0x63,0x68,0x6c,0x62,0x6b,0x2d,0x52,0x6f,0x6d, +0x61,0x6e,0x00,0x43,0x65,0x6e,0x74,0x75,0x72,0x79,0x53, +0x63,0x68,0x6c,0x62,0x6b,0x2d,0x42,0x6f,0x6c,0x64,0x00, +0x43,0x65,0x6e,0x74,0x75,0x72,0x79,0x53,0x63,0x68,0x6c, +0x62,0x6b,0x2d,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x43, +0x65,0x6e,0x74,0x75,0x72,0x79,0x53,0x63,0x68,0x6c,0x62, +0x6b,0x2d,0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c,0x69, +0x63,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x52,0x6f,0x6d, +0x61,0x6e,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x42,0x6f, +0x6c,0x64,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x49,0x74, +0x61,0x6c,0x69,0x63,0x00,0x44,0x75,0x74,0x63,0x68,0x2d, +0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c,0x69,0x63,0x00, +0x53,0x77,0x69,0x73,0x73,0x00,0x53,0x77,0x69,0x73,0x73, +0x2d,0x42,0x6f,0x6c,0x64,0x00,0x53,0x77,0x69,0x73,0x73, +0x2d,0x4f,0x62,0x6c,0x69,0x71,0x75,0x65,0x00,0x53,0x77, +0x69,0x73,0x73,0x2d,0x42,0x6f,0x6c,0x64,0x4f,0x62,0x6c, +0x69,0x71,0x75,0x65,0x00,0x53,0x77,0x69,0x73,0x73,0x2d, +0x4e,0x61,0x72,0x72,0x6f,0x77,0x00,0x53,0x77,0x69,0x73, +0x73,0x2d,0x4e,0x61,0x72,0x72,0x6f,0x77,0x2d,0x42,0x6f, +0x6c,0x64,0x00,0x53,0x77,0x69,0x73,0x73,0x2d,0x4e,0x61, +0x72,0x72,0x6f,0x77,0x2d,0x4f,0x62,0x6c,0x69,0x71,0x75, +0x65,0x00,0x53,0x77,0x69,0x73,0x73,0x2d,0x4e,0x61,0x72, +0x72,0x6f,0x77,0x2d,0x42,0x6f,0x6c,0x64,0x4f,0x62,0x6c, +0x69,0x71,0x75,0x65,0x00,0x5a,0x61,0x70,0x66,0x43,0x61, +0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x2d, +0x52,0x6f,0x6d,0x61,0x6e,0x00,0x5a,0x61,0x70,0x66,0x43, +0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63, +0x2d,0x42,0x6f,0x6c,0x64,0x00,0x5a,0x61,0x70,0x66,0x43, +0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63, +0x2d,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x5a,0x61,0x70, +0x66,0x43,0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68, +0x69,0x63,0x2d,0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c, +0x69,0x63,0x00,0x5a,0x61,0x70,0x66,0x43,0x68,0x61,0x6e, +0x63,0x65,0x72,0x79,0x2d,0x4d,0x65,0x64,0x69,0x75,0x6d, +0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x00,0x09,0x00,0x0A, +0x00,0x0B,0x00,0x0E,0x00,0x14,0x00,0x17,0x00,0x18,0x00, +0x1F,0x00,0x20,0x00,0x36,0x00,0x37,0x00,0x38,0x00,0x45,0x00, +0x47,0x00,0x48,0x00,0x80,0x00,0x82,0x00,0x83,0x00,0x84, +0x00,0x85,0x00,0x87,0x00,0x8B,0x00,0x8C,0x00,0x8D,0x00,0x8E, +0x00,0x8F,0x00,0x90,0x00,0x91,0x00,0x92,0x00,0x95,0x00,0x96, +0x00,0x97,0x00,0x98,0x00,0x99,0x00,0x9C,0x00,0x9E,0x00, +0x9F,0x00,0xA0,0x00,0xA1,0x00,0xA2,0x00,0xA3,0x00,0xCB,0x00, +0xCC,0x00,0xCD,0x00,0xCE,0x00,0xD1,0x00,0xD3,0x00,0xD4, +0x00,0xD5,0x00,0xD6,0x00,0xFA,0x00,0xFB,0x00,0xFC,0x00,0xFD, +0x00,0xCF,0x00,0x26,0x00,0x7E,0x03,0x05,0x00,0xA5,0x00, +0xA6,0x00,0xA8,0x00,0xAA,0x00,0xAD,0x00,0xAE,0x00,0xAF,0x00, +0xB0,0x00,0xB1,0x00,0xB2,0x00,0xB3,0x00,0xB5,0x00,0xB6,0x00, +0xB8,0x00,0xB9,0x00,0xBA,0x00,0xBB,0x00,0xBC,0x00,0xBE, +0x00,0xBF,0x00,0xC0,0x00,0xC1,0x00,0xC6,0x00,0xDC,0x00,0xEB, +0x00,0xEC,0x00,0xF2,0x00,0xF3,0x00,0x15,0x00,0x16,0x00, +0x86 +}; diff --git a/contrib/groff/src/devices/grolbp/grolbp.man b/contrib/groff/src/devices/grolbp/grolbp.man new file mode 100644 index 0000000..d567c1a --- /dev/null +++ b/contrib/groff/src/devices/grolbp/grolbp.man @@ -0,0 +1,357 @@ +'\" t +.\" The above line should force the use of tbl as a preprocessor +.\" vim: set syntax=nroff : +.\" The above line should set vim into nroff mode +.ig +Copyright (C) 1994-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. + +Modified by Francisco Andrés Verdú <pandres@dragonet.es> for the grolbp +program. +.. +.de TQ +.br +.ns +.TP \\$1 +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROLBP @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grolbp \- groff driver for Canon CAPSL printers (LBP-4 and LBP-8 series laser printers). +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fBgrolbp 'u +.ti \niu +.B grolpb +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-l +.OP \-\-landscape +.OP \-v +.OP \-\-version +.OP \-c n +.OP \-\-copies= numcopies +.OP \-p paper_size +.OP \-\-papersize= paper_size +.OP \-o orientation +.OP \-\-orientation= orientation +.OP \-F dir +.OP \-\-fontdir= dir +.OP \-h +.OP \-\-help +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.SH DESCRIPTION +.B grolbp +is a driver for +.B groff +that produces output in CAPSL and VDM format suitable for Canon LBP\-4 and +LBP\-8 printers. +.LP +For compatibility with grolj4 there is an additional drawing command +available: +.TP +.BI \eD'R\ dh\ dv ' +Draw a rule (i.e.\ a solid black rectangle), with one corner at the current +position, and the diagonally opposite corner at the current position +.RI +( dh , dv ). +.SH OPTIONS +Note that there can be whitespace between a one-letter option and its +argument; on the other hand, there must be whitespace and/or an equal sign +(`=') between a long-name option and its argument. +.TP +.BI \-c numcopies +.TQ +.BI \-\-copies= numcopies +Print +.I numcopies +copies of each page. +.TP +.B \-l +.TQ +.B \-\-landscape +Print the document with a landscape orientation. +.TP +.BI \-p paper_size +.TQ +.BI \-\-papersize= paper_size +Set the paper size to +.IR paper_size , +which must be a valid paper size description as indicated in the section +.BR "PAPER SIZES" . +.TP +.BI \-o orientation +.TQ +.BI \-\-orientation= orientation +Print the document with +.I orientation +orientation, which must be `portrait' or `landscape'. +.TP +.B \-v +.TQ +.B \-\-version +Print the version number. +.TP +.BI \-F dir +.TQ +.BI \-\-fontdir= dir +Prepend directory +.IB dir /devlbp +to the search path for font and device description files. +.TP +.B \-h +.TQ +.B \-\-help +Print a short help text. +.SH TYPEFACES +The driver supports the Dutch, Swiss and Swiss-Narrow scalable typefaces, +each one in the Regular, Bold, Italic and Bold-Italic styles. +Additionally, the Courier and Elite monospaced typefaces at the sizes 8 and +12 points (for Courier) resp. 8 and 10 points (for Elite) are supported, +each one in the Regular, Bold and Italic styles. +.PP +The following chart summarizes the font names you can use to access these +fonts: +.PP +.TS +tab(|) allbox center; +c c c c c +ab c c c c +. +Typeface | Regular | Bold | Italic | Bold-Italic +Dutch | TR | TB | TI | TBI +Swiss | HR | HB | HI | HBI +Swiss Narrow | HNR | HNB | HNI | HNBI +Courier | CR | CB | CI | +Elite | ER | EB | EI | +.TE +.PP +.SH PAPER SIZES +The paper size can be set in the +.B DESC +file or with command line options to +.BR grolbp . +If the paper size is specified both ways, the command line options take +precedence over the contents of the +.B DESC +file (this applies to the page orientation too). +.PP +To set the paper size in the +.B DESC +file, insert in that file a line containing +.B papersize +.IR desired_papersize , +where +.I desired_papersize +is: +.IP \(bu 4 +One of the recognized paper sizes: `a4', `letter', `legal' or `executive'. +.IP \(bu 4 +A custom defined paper size, as described in the +.B CUSTOM PAPER SIZES +subsection below. +.IP \(bu 4 +The name of a file (e.g. +.IR /etc/papersize ) +whose first line must be the desired paper size in one of the above formats. +.PP +If there are various papersize lines in the +.B DESC +file, only the first valid one is used. +.PP +To set the paper size in the command line, add +.sp 1 +.in +2m +.BI \-p \ desired_papersize +.in -2m +.sp 1 +or +.sp 1 +.in +2m +.BI \-\-papersize= desired_papersize +.in -2m +.sp 1 +to the other +.B grolbp +options, where +.B desired_papersize +is in the same format as in the +.B DESC +file. +.PP +Paper sizes are case insensitive (i.e., `A4' is the same as `a4'). +.PP +If no paper size is specified in the +.B DESC +file or the command line, a default size of A4 is used. +.TP +.SH CUSTOM PAPER SIZES +Custom defined paper sizes are in the form +.BI cust length x width +where +.I length +and +.I width +are the dimensions of the paper you want to to use, specified in printer +units (1/300 of an inch). +For instance, to print in a postcard sized paper which is two inches long +and four inches wide you can insert a line containing +.sp 1 +.in +2m +.B papersize cust600x1200 +.in -2m +.sp 1 +at the beginning of the +.B DESC +file. +.SH PAGE ORIENTATION +As with the page size, the orientation of the printed page +.RB ( portrait +or +.BR landscape ) +can be set in the +.B DESC +file or with command line options. +It is also case insensitive. +.PP +To set the orientation in the +.B DESC +file, insert a line with the following content: +.sp 1 +.in +2m +.B orientation +.RB [ portrait | landscape ] +.in -2m +.sp 1 +As with paper sizes, only the first valid orientation command in the +.B DESC +file is used. +.PP +To set the page orientation with command line options you can use the +.B \-o +or +.B \-\-orientation +option with the same parameters +.RB ( portrait +or +.BR landscape ) +as in the +.B DESC +file. +Or you can use the +.B \-l +option to force the pages to be printed in landscape. +.SH FONT FILE FORMAT +In addition to the usual commands described in +.BR groff_font (@MAN5EXT@), +.B grolbp +provides the command +.I lbpname +which sets the font name sent to the printer when requesting this font. +The syntax of this command is: +.sp 1 +.in +2m +.B lbpname +.I printer_font_name +.in -2m +.IP \(bu +For bitmapped fonts, +.I printer_font_name +has the form +.sp 1 +.in +2m +.RI N\(la base_fontname \(ra\(la font_style \(ra +.in -2m +.sp 1 +.I base_fontname +is the font name as it appears in the printers font listings without the +first letter, up to (but not including) the font size. +.I font_style +can be one of the letters +.BR R , +.BR I , +or +.BR B , +indicating the font styles Roman, Italic and Bold respectively. +.IP +For instance, if the printer's +.I font listing A +shows font `Nelite12I.ISO_USA', the corresponding entry in the font +description file is +.sp 1 +.in +2m +.B lbpname NeliteI +.in -2m +.IP +Note that you may need to modify +.B grolbp +to add support for new bitmapped fonts, since the available font names and +font sizes of bitmapped fonts (as documented above) are hard-coded into the +program. +.IP \(bu +For scalable fonts, +.I printer_font_name +is identical to the font name as it appears in the printer's +.IR "font listing A" . +.IP +For instance, to select the `Swiss' font in bold style, which appears in +the printer's +.I font listing A +as `Swiss-Bold', the required +.B lbpname +command line is +.sp 1 +.in +2m +.B lbpname Swiss-Bold +.in -2m +.sp 1 +.PP +The argument of +.B lbpname +is case sensitive. +.SH FILES +.TP +.B @FONTDIR@/devlbp/DESC +Device description file. +.TP +.BI @FONTDIR@/devlbp/ F +Font description file for font +.IR F . +.TP +.B @MACRODIR@/lbp.tmac +Macros for use with +.BR grolbp . +.SH SEE ALSO +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) +.\" +.\" Local Variables: +.\" mode: nroff +.\" End: diff --git a/contrib/groff/src/devices/grolbp/lbp.cc b/contrib/groff/src/devices/grolbp/lbp.cc new file mode 100644 index 0000000..69196af --- /dev/null +++ b/contrib/groff/src/devices/grolbp/lbp.cc @@ -0,0 +1,765 @@ +// -*- C++ -*- +/* Copyright (C) 1994, 2000, 2001 Free Software Foundation, Inc. + Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas + taken from the other groff drivers. + + +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* +TODO + + - Add X command to include bitmaps +*/ +#define _GNU_SOURCE + +#include "driver.h" +#include "lbp.h" +#include "charset.h" + +#include "nonposix.h" + +static short int papersize = -1, // papersize + orientation = -1 , // orientation + paperlength = 0, // Custom Paper size + paperwidth = 0, + ncopies = 1; // Number of copies + +class lbp_font : public font { +public: + ~lbp_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static lbp_font *load_lbp_font(const char *); + char *lbpname; + char is_scalable; +private: + lbp_font(const char *); +}; + +class lbp_printer : public printer { +public: + lbp_printer(); + ~lbp_printer(); + void set_char(int, font *, const environment *, int, const char *name); + void draw(int code, int *p, int np, const environment *env); + void begin_page(int); + void end_page(int page_length); + font *make_font(const char *); + void end_of_line(); +private: + void set_line_thickness(int size, int dot = 0); + void vdmstart(); + void vdmflush(); // the name vdmend was already used in lbp.h + void setfillmode(int mode); + void polygon( int hpos,int vpos,int np,int *p); + char *font_name(const lbp_font *f, const int siz); + + int fill_pattern; + int fill_mode; + int cur_hpos; + int cur_vpos; + lbp_font *cur_font; + int cur_size; + unsigned short cur_symbol_set; + int line_thickness; +}; + +// Compatibility section. +// +// Here we define some functions not present in some of the targets +// platforms +#ifndef HAVE_STRSEP +// Solaris 8 doesn't have the strsep function +static char *strsep(char **pcadena, const char *delim) +{ + char *p; + + p = strtok(*pcadena,delim); + *pcadena = strtok(NULL,delim); + return p; + +}; +#endif + +#ifndef HAVE_STRDUP +// Ditto with OS/390 and strdup +static char *strdup(const char *s) +{ + char *result; + + result = (char *)malloc(strlen(s)+1); + if (result != NULL) strcpy(result,s); + return result; + +}; // strdup + +#endif +lbp_font::lbp_font(const char *nm) +: font(nm) +{ +} + +lbp_font::~lbp_font() +{ +} + +lbp_font *lbp_font::load_lbp_font(const char *s) +{ + lbp_font *f = new lbp_font(s); + f->lbpname = NULL; + f->is_scalable = 1; // Default is that fonts are scalable + if (!f->load()) { + delete f; + return 0; + } + return f; +} + + +void lbp_font::handle_unknown_font_command(const char *command, + const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "lbpname") == 0) { + if (arg == 0) + fatal_with_file_and_line(filename, lineno, + "`%1' command requires an argument", + command); + this->lbpname = new char[strlen(arg)+1]; + strcpy(this->lbpname,arg); + // We Recongnize bitmaped fonts by the first character of it's name + if (arg[0] == 'N') this->is_scalable = 0; + // fprintf(stderr,"Loading font \"%s\" \n",arg); + }; // if (strcmp(command, "lbpname") + // fprintf(stderr,"Loading font %s \"%s\" in %s at %d\n",command,arg,filename,lineno); +}; + +static void wp54charset() +{ + int i; + + lbpputs("\033[714;100;29;0;32;120.}"); + for (i = 0; i < sizeof(symset) ; i++) lbpputc(symset[i]); + lbpputs("\033[100;0 D"); + return ; +}; + +lbp_printer::lbp_printer() +: fill_pattern(1), + fill_mode(0), + cur_hpos(-1), + cur_font(0), + cur_size(0), + cur_symbol_set(0), + line_thickness(-1) +{ +#ifdef SET_BINARY + SET_BINARY(fileno(stdout)); +#endif + lbpinit(stdout); + lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h"); + wp54charset(); // Define the new symbol set + lbpputs("\033[7 I\033[?32h\033[?33h\033[11h"); + // Paper size handling + if (orientation < 0) orientation = 0;// Default orientation is portrait + if (papersize < 0) papersize = 14; // Default paper size is A4 + if (papersize < 80) // standard paper + lbpprintf("\033[%dp",(papersize | orientation)); + else // Custom paper + lbpprintf("\033[%d;%d;%dp",(papersize | orientation),\ + paperlength,paperwidth); + + // Number of copies + lbpprintf("\033[%dv\n",ncopies); + + lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\"); + lbpmoveabs(0,0); + lbpputs("\033[0t\033[2t"); + lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML + // Secondary symbol set IBMR1 + cur_symbol_set = 0; +}; + +lbp_printer::~lbp_printer() +{ + lbpputs("\033P1y\033\\"); + lbpputs("\033c\033<"); +} + +void lbp_printer::begin_page(int) +{ +} + +void lbp_printer::end_page(int) +{ + if (vdminited()) vdmflush(); + lbpputc('\f'); + cur_hpos = -1; +} + +void lbp_printer::end_of_line() +{ + cur_hpos = -1; // force absolute motion +} + +char *lbp_printer::font_name(const lbp_font *f, const int siz) +{ + static char bfont_name[255] ; // The resulting font name + char type, // Italic, Roman, Bold + ori, // Normal or Rotated + *nam; // The font name without other data. +// nam[strlen(f->lbpname)-2]; // The font name without other data. + int cpi; // The font size in characters per inch + // (Bitmaped fonts are monospaced). + + + /* Bitmap font selection is ugly in this printer, so don't expect + this function to be elegant. */ + + bfont_name[0] = 0x00; + if (orientation) // Landscape + ori = 'R'; + else // Portrait + ori = 'N'; + type = f->lbpname[strlen(f->lbpname)-1]; + nam = new char[strlen(f->lbpname)-2]; + strncpy(nam,&(f->lbpname[1]),strlen(f->lbpname)-2); + nam[strlen(f->lbpname)-2] = 0x00; + // fprintf(stderr,"Bitmap font '%s' %d %c %c \n",nam,siz,type,ori); + /* Since these fonts are avaiable only at certain sizes, + 10 and 17 cpi for courier, 12 and 17 cpi for elite, + we adjust the resulting size. */ + cpi = 17; + // Fortunately there were only two bitmaped fonts shiped with the printer. + if (!strcasecmp(nam,"courier")) + { // Courier font + if (siz >= 12) cpi = 10; + else cpi = 17; + }; + if (!strcasecmp(nam,"elite")) + { // Elite font + if (siz >= 10) cpi = 12; + else cpi = 17; + }; + + // Now that we have all the data, let's generate the font name. + if ((type != 'B') && (type != 'I')) // Roman font + sprintf(bfont_name,"%c%s%d",ori,nam,cpi); + else + sprintf(bfont_name,"%c%s%d%c",ori,nam,cpi,type); + + return bfont_name; + +}; // lbp_printer::font_name + +void lbp_printer::set_char(int index, font *f, const environment *env, int w, const char *name) +{ + int code = f->get_code(index); + + unsigned char ch = code & 0xff; + unsigned short symbol_set = code >> 8; + if (f != cur_font) { + lbp_font *psf = (lbp_font *)f; + // fprintf(stderr,"Loading font %s \"%d\" \n",psf->lbpname,env->size); + if (psf->is_scalable) + { // Scalable font selection is different from bitmaped + lbpprintf("\033Pz%s.IBML\033\\\033[%d C",psf->lbpname,\ + (int)((env->size*300)/72)); + } else + { // Bitmaped font + lbpprintf("\033Pz%s.IBML\033\\\n",font_name(psf,env->size)); + }; + lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set + cur_size = env->size; + cur_font = psf; + cur_symbol_set = 0; + } + if (symbol_set != cur_symbol_set) { + if ( cur_symbol_set == 3 ) { + // if current symbol set is Symbol we must restore the font + lbpprintf("\033Pz%s.IBML\033\\\033[%d C",cur_font->lbpname,\ + (int)((env->size*300)/72)); + }; // if ( cur_symbol_set == 3 ) + switch (symbol_set) { + case 0: lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets + break; + case 1: lbpputs("\033(d\033)' 1"); // Select wp54 symbol set + break; + case 2: lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set + break; + case 3: lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",\ + (int)((env->size*300)/72)); + lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font + break; + case 4: lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set + break; + }; // switch (symbol_set) + +// if (symbol_set == 1) lbpputs("\033(d"); // Select wp54 symbol set +// else lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets + cur_symbol_set = symbol_set; + } + if (env->size != cur_size) { + + if (!cur_font->is_scalable) + lbpprintf("\033Pz%s.IBML\033\\\n",font_name(cur_font,env->size)); + else + lbpprintf("\033[%d C",(int)((env->size*300)/72)); + cur_size = env->size; + } + if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos)) + { + // lbpmoveabs(env->hpos - ((5*300)/16),env->vpos ); + lbpmoveabs(env->hpos - 64,env->vpos - 64 ); + cur_vpos = env->vpos; + cur_hpos = env->hpos; + }; + if ((ch & 0x7F) < 32) lbpputs("\033[1.v"); + lbpputc(ch); + cur_hpos += w; +}; + +void +lbp_printer::vdmstart() +{ + FILE *f; + static int changed_origin = 0; + + errno = 0; + f = tmpfile(); + // f = fopen("/tmp/gtmp","w+"); + if (f == NULL) perror("Openinig temp file"); + vdminit(f); + if (!changed_origin) { // we should change the origin only one time + changed_origin = 1; + vdmorigin(-63,0); + }; + vdmlinewidth(line_thickness); + +}; + +void +lbp_printer::vdmflush() +{ + char buffer[1024]; + int bytes_read = 1; + + vdmend(); + fflush(lbpoutput); + /* lets copy the vdm code to the output */ + rewind(vdmoutput); + do + { + bytes_read = fread(buffer,1,sizeof(buffer),vdmoutput); + bytes_read = fwrite(buffer,1,bytes_read,lbpoutput); + } while ( bytes_read == sizeof(buffer)); + + fclose(vdmoutput); // This will also delete the file, + // since it is created by tmpfile() + vdmoutput = NULL; + +}; // lbp_printer::vdmflush + +inline void +lbp_printer::setfillmode(int mode) +{ + if (mode != fill_mode) { + if (mode != 1) vdmsetfillmode(mode,1,0); + else vdmsetfillmode(mode,1,1); // To get black we must use white + // inverted + fill_mode = mode; + }; +}; // setfillmode + +inline void +lbp_printer::polygon( int hpos,int vpos,int np,int *p) +{ + //int points[np+2],i; + int *points,i; + + points = new int[np+2]; + points[0] = hpos; + points[1] = vpos; +/* fprintf(stderr,"Poligon (%d,%d) ", points[0],points[1]);*/ + for (i = 0; i < np; i++) points[i+2] = p[i]; +/* for (i = 0; i < np; i++) fprintf(stderr," %d ",p[i]); + fprintf(stderr,"\n"); */ + vdmpolygon((np /2) + 1,points); +}; + +void lbp_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + case 't': + if (np == 0) line_thickness = 1; + else { // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } // if (np != ... + if (p[0] == 0) line_thickness = 1; + if (p[0] < 0) // Default = 1 point + line_thickness = (int)(env->size*30/72); + line_thickness = (int)((abs(p[0])*env->size)/10); + if ((line_thickness > 16 ) && (!vdminited())) + { /* for greater thickness we must use VDM */ + vdmstart(); + /* vdmlinewidth(line_thickness); already done in + * vdmstart() */ + }; + if (vdminited()) vdmlinewidth(line_thickness); + // fprintf(stderr,"\nthickness: %d == %d, size %d\n", + // p[0],line_thickness,env->size ); + } // else + break; + + case 'l': // Line + if (np != 2) { + error("2 arguments required for line"); + break; + }; + if (!vdminited()) vdmstart(); + vdmline(env->hpos,env->vpos,p[0],p[1]); + /*fprintf(stderr,"\nline: %d,%d - %d,%d thickness %d == %d\n",\ + env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],\ + env->vpos -64 + p[1],env->size, line_thickness);*/ + break; + case 'R': // Rule + if (np != 2) { + error("2 arguments required for Rule"); + break; + } + if (vdminited()) { + setfillmode(fill_pattern); // Solid Rule + vdmrectangle(env->hpos,env->vpos,p[0],p[1]); + } + else { + lbpruleabs(env->hpos - 64,env->vpos -64 , p[0], p[1]); + cur_vpos = p[1]; + cur_hpos = p[0]; + }; + fprintf(stderr,"\nrule: thickness %d == %d\n", env->size, line_thickness); + break; + case 'P': // Filled Polygon + if (!vdminited()) vdmstart(); + setfillmode(fill_pattern); + polygon(env->hpos,env->vpos,np,p); + break; + case 'p': // Empty Polygon + if (!vdminited()) vdmstart(); + setfillmode(0); + polygon(env->hpos,env->vpos,np,p); + break; + case 'C': // Filled Circle + if (!vdminited()) vdmstart(); + // fprintf(stderr,"Circle (%d,%d) Fill %d\n",env->hpos,env->vpos,fill_pattern); + setfillmode(fill_pattern); + vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2); + break; + case 'c': // Empty Circle + if (!vdminited()) vdmstart(); + setfillmode(0); + vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2); + break; + case 'E': // Filled Ellipse + if (!vdminited()) vdmstart(); + setfillmode(fill_pattern); + vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0); + break; + case 'e': // Empty Ellipse + if (!vdminited()) vdmstart(); + setfillmode(0); + vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0); + break; + case 'a': // Arc + if (!vdminited()) vdmstart(); + setfillmode(0); + // VDM draws arcs clockwise and pic counterclockwise + // We must compensate for that, exchanging the starting and + // ending points + vdmvarc(env->hpos + p[0],env->vpos+p[1],\ + int(sqrt( double((p[0]*p[0])+(p[1]*p[1])))),\ + p[2],p[3],\ + (-p[0]),(-p[1]),1,2); + break; + case '~': // Spline + if (!vdminited()) vdmstart(); + setfillmode(0); + vdmspline(np/2,env->hpos,env->vpos,p); + break; + case 'f': + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + }; + // fprintf(stderr,"Fill %d\n",p[0]); + if ((p[0] == 1) || (p[0] >= 1000)) { // Black + fill_pattern = 1; + break; + }; // if (p[0] == 1) + if (p[0] == 0) { // White + fill_pattern = 0; + break; + }; + if ((p[0] > 1) && (p[0] < 1000)) + { + if (p[0] >= 990) fill_pattern = -23; + else if (p[0] >= 700) fill_pattern = -28; + else if (p[0] >= 500) fill_pattern = -27; + else if (p[0] >= 400) fill_pattern = -26; + else if (p[0] >= 300) fill_pattern = -25; + else if (p[0] >= 200) fill_pattern = -22; + else if (p[0] >= 100) fill_pattern = -24; + else fill_pattern = -21; + }; // if (p[0] >= 0 && p[0] <= 1000) + break; + default: + error("unrecognised drawing command `%1'", char(code)); + break; + }; // switch (code) + return ; +}; + +font *lbp_printer::make_font(const char *nm) +{ + return lbp_font::load_lbp_font(nm); +} + + + +printer *make_printer() +{ + return new lbp_printer; +} + +static struct +{ + const char *name; + int code; +} papersizes[] = +{{ "A4", 14 }, +{ "letter", 30 }, +{ "legal", 32 }, +{ "executive", 40 }, +}; + + +static int set_papersize(const char *papersize) +{ + int i; + + // First test for a standard (i.e. supported directly by the printer) + // papersize + for (i = 0 ; i < sizeof(papersizes)/sizeof(papersizes[0]); i++) + { + if (strcasecmp(papersizes[i].name,papersize) == 0) + return papersizes[i].code; + }; + + // Now test for a custom papersize + if (strncasecmp("cust",papersize,4) == 0) + { + char *p , + *p1, + *papsize; + + p = papsize = strdup(&papersize[4]); + if (papsize == NULL) return -1; + p1 = strsep(&p,"x"); + if (p == NULL) + { // let's test for an uppercase x + p = papsize ; + p1 = strsep(&p,"X"); + if (p == NULL) { free(papsize); return -1;}; + }; // if (p1 == NULL) + paperlength = atoi(p1); + if (paperlength == 0) { free(papsize); return -1;}; + paperwidth = atoi(p); + if (paperwidth == 0) { free(papsize); return -1;}; + free(papsize); + return 82; + }; // if (strcnasecmp("cust",papersize,4) == 0) + + return -1; +}; + +static int handle_papersize_command(const char *arg) +{ + int n = set_papersize(arg); + + if (n < 0) + { // If is not a standard nor custom paper size + // let's see if it's a file (i.e /etc/papersize ) + FILE *f = fopen(arg,"r"); + if (f != NULL) + { // the file exists and is readable + char psize[255],*p; + fgets(psize,254,f); + fclose(f); + // set_papersize doesn't like the trailing \n + p = psize; while (*p) p++; + if (*(--p) == '\n') *p = 0x00; + + n = set_papersize(psize); + }; // if (f != NULL) + }; // if (n < 0) + + return n; +}; // handle_papersize_command + + +static void handle_unknown_desc_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + // papersize command + if (strcasecmp(command, "papersize") == 0) { + // We give priority to command line options + if (papersize > 0) return; + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`papersize' command requires an argument"); + else + { + int n = handle_papersize_command(arg); + if (n < 0) + error_with_file_and_line(filename, lineno, + "unknown paper size `%1'", arg); + else + papersize = n; + + }; // if (arg == 0) ... else ... + }; // if (strcasecmp(command, "papersize") + + // orientation command + if (strcasecmp(command, "orientation") == 0) { + // We give priority to command line options + if (orientation > 0) return; + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`papersize' command requires an argument"); + else { + if (strcasecmp(arg,"portrait") == 0) orientation = 0; + else { if (strcasecmp(arg,"landscape") == 0) orientation = 1; + else error_with_file_and_line(filename, lineno, + "`orientation' command requires an argument"); + }; + }; // if (arg == 0) ... else ... + }; // if (strcasecmp(command, "orientation") == 0) +}; + +static struct option long_options[] = { + { "orientation", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'v' }, + { "copies", required_argument, NULL, 'c' }, + { "landscape", no_argument, NULL, 'l' }, + { "papersize", required_argument, NULL, 'p' }, + { "fontdir", required_argument, NULL, 'F' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, 0, 0 } + }; + +static void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or] "\ + " [files ...]\n"\ + " -o --orientation=[portrait|landscape]\n"\ + " -v --version\n"\ + " -c --copies=numcopies\n"\ + " -l --landscape\n"\ + " -p --papersize=paper_size\n"\ + " -F --fontdir=dir\n"\ + " -h --help\n", + program_name); +}; // usage + +int main(int argc, char **argv) +{ + if (program_name == NULL) program_name = strdup(argv[0]); + + font::set_unknown_desc_command_handler(handle_unknown_desc_command); + // command line parsing + int c = 0; + int option_index = 0; + + while (c >= 0 ) + { + c = getopt_long (argc, argv, "F:p:lvo:c:h",\ + long_options, &option_index); + switch (c) { + case 'F' : font::command_line_font_dir(optarg); + break; + case 'p' : { + int n = handle_papersize_command(optarg); + if (n < 0) + error("unknown paper size `%1'", optarg); + else + papersize = n; + break; + }; + case 'l' : orientation = 1; + break; + case 'v' : { + extern const char *Version_string; + printf("GNU grolbp (groff) version %s\n", + Version_string); + exit(0); + break; + }; + case 'o' : { + if (strcasecmp(optarg,"portrait") == 0) + orientation = 0; + else { + if (strcasecmp(optarg,"landscape") == 0) + orientation = 1; + else + error("unknown orientation '%1'", optarg); + }; + break; + }; + case 'c' : { + char *ptr; + long n = strtol(optarg, &ptr, 10); + if ((n <= 0) && (ptr == optarg)) + error("argument for -c must be a positive integer"); + else if (n <= 0 || n > 32767) + error("out of range argument for -c"); + else + ncopies = unsigned(n); + break; + } + case 'h' : usage(stdout); + exit(0); + break; + case '?' : usage(stderr); + exit(1); + break; + + }; // switch (c) + }; // while (c > 0 ) + + if (optind >= argc) + do_file("-"); + + while (optind < argc) { + do_file(argv[optind++]); + }; + + lbpputs("\033c\033<"); + return 0; +}; diff --git a/contrib/groff/src/devices/grolbp/lbp.h b/contrib/groff/src/devices/grolbp/lbp.h new file mode 100644 index 0000000..6a11b19 --- /dev/null +++ b/contrib/groff/src/devices/grolbp/lbp.h @@ -0,0 +1,511 @@ +// -*- C -*- +/* Copyright (C) 1994, 2000 Free Software Foundation, Inc. + Written by Francisco Andrés Verdú <pandres@dragonet.es> + +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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This file contains a set of utility functions to use canon CAPSL printers + * (lbp-4 and lbp-8 series printers) */ + +#ifndef LBP_H +#define LBP_H + +#include <stdio.h> +#include <stdarg.h> + +static FILE *lbpoutput = NULL; +static FILE *vdmoutput = NULL; + +static inline void +lbpinit(FILE *outfile) +{ + lbpoutput = outfile; +}; + + +static inline void +lbpprintf(char *format, ... ) +{ /* Taken from cjet */ + va_list stuff; + + va_start(stuff, format); + vfprintf(lbpoutput, format, stuff); + va_end(stuff); +}; + +static inline void +lbpputs(char *data) +{ + fputs(data,lbpoutput); +}; + +static inline void +lbpputc(char c) +{ + fputc(c,lbpoutput); +}; + + +static inline void +lbpsavestatus(int index ) +{ + fprintf(lbpoutput,"\033[%d%%y",index); +}; + +static inline void +lbprestorestatus(int index ) +{ + fprintf(lbpoutput,"\033[%d%cz",index ,'%'); +}; + +static inline void +lbpsavepos(int index) +{ + fprintf(lbpoutput,"\033[1;%d;0x",index); +}; + +static inline void +lbprestorepos(int index) +{ + fprintf(lbpoutput,"\033[0;%d;0x",index); +}; + +static inline void +lbprestoreposx(int index) +{ + fprintf(lbpoutput,"\033[0;%d;1x",index); +}; + +static inline void +lbpmoverel(int despl, char direction) +{ + fprintf(lbpoutput,"\033[%d%c",despl,direction); +}; + +static inline void +lbplinerel(int width,int despl,char direction ) +{ + fprintf(lbpoutput,"\033[%d;0;9{\033[%d%c\033[9}",width,despl,direction); +}; + +static inline void +lbpmoveabs(int x, int y) +{ + fprintf(lbpoutput,"\033[%d;%df",y,x); +}; + +static inline void +lbplineto(int x,int y, int width ) +{ + fprintf(lbpoutput,"\033[%d;0;9{",width); + lbpmoveabs(x,y); + fprintf(lbpoutput,"\033[9}\n"); +}; + +static inline void +lbpruleabs(int x, int y, int hsize, int vsize) +{ + lbpmoveabs(x,y); + fprintf(lbpoutput,"\033[0;9;000s"); + lbpmoveabs(x+hsize,y+vsize); + fprintf(lbpoutput,"\033[9r"); +}; + +static inline void vdmprintf(char *format, ... ); + +static inline char * +vdmnum(int num,char *result) +{ + char b1,b2,b3; + char *p = result; + int nm; + + nm = abs(num); + /* First byte 1024 - 32768 */ + b1 = ((nm >> 10) & 0x3F); + if (b1) *p++ = b1 | 0x40; + + /* Second Byte 16 - 1024 */ + b2 = ((nm >> 4) & 0x3F); + if ( b1 || b2) *p++= b2 | 0x40; + + /* Third byte 0 - 15 */ + b3 = ((nm & 0x0F) | 32); + if (num >= 0) b3 |= 16; + *p++ = b3; + *p = 0x00; /* End of the resulting string */ + return result; +}; + +static inline void +vdmorigin(int newx, int newy) +{ + char nx[4],ny[4]; + + vdmprintf("}\"%s%s\x1e",vdmnum(newx,nx),vdmnum(newy,ny)); +}; /* vdmorigin */ + + +static inline FILE * +vdminit(FILE *vdmfile) +{ + char scale[4],size[4],lineend[4]; + +/* vdmoutput = tmpfile();*/ + vdmoutput = vdmfile; + /* Initialize the VDM mode */ + vdmprintf("\033[0&}#GROLBP\x1e!0%s%s\x1e$\x1e}F%s\x1e",\ + vdmnum(-3,scale),vdmnum(1,size),vdmnum(1,lineend)); + return vdmoutput; + +}; + +static inline void +vdmend() +{ + vdmprintf("}p\x1e"); +}; + +static inline void +vdmprintf(char *format, ... ) +{ /* Taken from cjet */ + va_list stuff; + + if (vdmoutput == NULL) vdminit(tmpfile()); + va_start(stuff, format); + vfprintf(vdmoutput, format, stuff); + va_end(stuff); +}; + +static inline void +vdmsetfillmode(int pattern,int perimeter, int inverted) +{ + char patt[4],perim[4], + rot[4], /* rotation */ + espejo[4], /* espejo */ + inv[4]; /* Inverted */ + + vdmprintf("I%s%s%s%s%s\x1e",vdmnum(pattern,patt),\ + vdmnum(perimeter,perim),vdmnum(0,rot), + vdmnum(0,espejo),vdmnum(inverted,inv)); +}; + +static inline void +vdmcircle(int centerx, int centery, int radius) +{ + char x[4],y[4],rad[4]; + + vdmprintf("5%s%s%s\x1e",vdmnum(centerx,x),vdmnum(centery,y),\ + vdmnum(radius,rad)); +}; + +static inline void +vdmaarc(int centerx, int centery, int radius,int startangle,int angle,int style,int arcopen) +{ + char x[4],y[4],rad[4],stx[4],sty[4],styl[4],op[4]; + + vdmprintf("}6%s%s%s%s%s%s%s\x1e",vdmnum(arcopen,op),\ + vdmnum(centerx,x),vdmnum(centery,y),\ + vdmnum(radius,rad),vdmnum(startangle,stx),vdmnum(angle,sty),\ + vdmnum(style,styl)); +}; + +static inline void +vdmvarc(int centerx, int centery,int radius, int startx, int starty, int endx, int endy,\ + int style,int arcopen) +{ + char x[4],y[4],rad[4],stx[4],sty[4],enx[4],eny[4],styl[4],op[4]; + + vdmprintf("}6%s%s%s%s%s%s%s%s\x1e",vdmnum(arcopen,op),\ + vdmnum(centerx,x),vdmnum(centery,y),\ + vdmnum(radius,rad),vdmnum(startx,stx),vdmnum(starty,sty),\ + vdmnum(endx,enx),vdmnum(endy,eny),vdmnum(style,styl)); +}; + +static inline void +vdmellipse(int centerx, int centery, int radiusx, int radiusy,int rotation) +{ + char x[4],y[4],radx[4],rady[4],rotat[4]; + + vdmprintf("}7%s%s%s%s%s\x1e\n",vdmnum(centerx,x),vdmnum(centery,y),\ + vdmnum(radiusx,radx),vdmnum(radiusy,rady),\ + vdmnum(rotation,rotat)); +}; + +static inline void +vdmsetlinetype(int lintype) +{ + char ltyp[4], expfact[4]; + + vdmprintf("E1%s%s\x1e",vdmnum(lintype,ltyp),vdmnum(1,expfact)); + +}; + +static inline void +vdmsetlinestyle(int lintype, int pattern,int unionstyle) +{ + char patt[4],ltip[4], + rot[4], /* rotation */ + espejo[4], /* espejo */ + in[4]; /* Inverted */ + + vdmprintf("}G%s%s%s%s%s\x1e",vdmnum(lintype,ltip),\ + vdmnum(pattern,patt),vdmnum(0,rot), + vdmnum(0,espejo),vdmnum(0,in)); + vdmprintf("}F%s",vdmnum(unionstyle,rot)); +}; + +static inline void +vdmlinewidth(int width) +{ + char wh[4]; + + vdmprintf("F1%s\x1e",vdmnum(width,wh)); +}; + +static inline void +vdmrectangle(int origx, int origy,int dstx, int dsty) +{ + char xcoord[4],ycoord[4],sdstx[4],sdsty[4]; + + vdmprintf("}:%s%s%s%s\x1e\n",vdmnum(origx,xcoord),vdmnum(dstx,sdstx),\ + vdmnum(origy,ycoord),vdmnum(dsty,sdsty)); +}; /* polyline */ + +static inline void +vdmpolyline(int numpoints, int *points) +{ + int i,*p = points; + char xcoord[4],ycoord[4]; + + if (numpoints < 2) return; + vdmprintf("1%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord)); + p += 2; + for (i = 1; i < numpoints ; i++) { + vdmprintf("%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord)); + p += 2; + }; /* for */ + vdmprintf("\x1e\n"); +}; /* polyline */ + +static inline void +vdmpolygon(int numpoints, int *points) +{ + int i,*p = points; + char xcoord[4],ycoord[4]; + + if (numpoints < 2) return; + vdmprintf("2%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord)); + p += 2; + for (i = 1; i < numpoints ; i++) { + vdmprintf("%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord)); + p += 2; + }; /* for */ + vdmprintf("\x1e\n"); + +}; /* vdmpolygon */ + + +/************************************************************************ + * Highter level auxiliary functions * + ************************************************************************/ +static inline int +vdminited() +{ + return (vdmoutput != NULL); +}; /* vdminited */ + + +static inline void +vdmline(int startx, int starty, int sizex, int sizey) +{ + int points[4]; + + points[0] = startx; + points[1] = starty; + points[2] = sizex; + points[3] = sizey; + + vdmpolyline(2,points); + +}; + +/*#define THRESHOLD .05 */ /* inch */ +#define THRESHOLD 1 /* points (1/300 inch) */ +static inline void +splinerel(double px,double py,int flush) +{ + static int lx = 0 ,ly = 0; + static float pend = 0.0; + static int dy = 0, despx = 0, despy = 0, sigpend = 0; + int dxnew ,dynew, sg; + char xcoord[4],ycoord[4]; + float npend ; + + if (flush == -1) {lx = (int)px; ly = (int)py; return;}; + + if (flush == 0) { + dxnew = (int)px -lx; + dynew = (int)py -ly; + if ((dxnew == 0) && (dynew == 0)) return; + sg = (dxnew < 0)? -1 : 0; +/* fprintf(stderr,"s (%d,%d) (%d,%d)\n",dxnew,dynew,despx,despy);*/ + if (dynew == 0) { + despx = dxnew; + if ((sg == sigpend) && (dy == 0)){ + return; + }; + dy = 0; + } + else { + dy = 1; + npend = (1.0*dxnew)/dynew; + if (( npend == pend) && (sigpend == sg)) + { despy = dynew; despx = dxnew; return; } + else + { sigpend = sg; + pend = npend; + }; /* else (( npend == pend) && ... */ + }; /* else (if (dynew == 0)) */ + }; /* if (!flush ) */ + + /* if we've changed direction we must draw the line */ +/* fprintf(stderr," (%d) %.2f,%.2f\n",flush,(float)px,(float)py);*/ + if ((despx != 0) || (despy != 0)) vdmprintf("%s%s",vdmnum(despx,xcoord),\ + vdmnum(despy,ycoord)); + /*if ((despx != 0) || (despy != 0)) fprintf(stderr,"2 + *%d,%d\n",despx,despy);*/ + if (flush) { + dxnew = dy = despx = despy = 0; + return; + }; /* if (flush) */ + dxnew -= despx; + dynew -= despy; + if ((dxnew != 0) || (dynew != 0)) vdmprintf("%s%s",vdmnum(dxnew,xcoord),\ + vdmnum(dynew,ycoord)); + +/* if ((dxnew != 0) || (dynew != 0)) fprintf(stderr,"3 + * %d,%d\n",dxnew,dynew);*/ + lx = (int)px; ly = (int)py; + dxnew = dy = despx = despy = 0; + +}; /* splinerel */ + +/********************************************************************** + * The following code to draw splines is adapted from the transfig package + */ +static void +quadratic_spline(double a1,double b1, double a2, double b2, \ + double a3, double b3, double a4, double b4) +{ + double x1, y1, x4, y4; + double xmid, ymid; + + x1 = a1; y1 = b1; + x4 = a4; y4 = b4; + xmid = (a2 + a3)/2.0; + ymid = (b2 + b3)/2.0; + if ((fabs(x1 - xmid) < THRESHOLD) && (fabs(y1 - ymid) < THRESHOLD)) { + splinerel(xmid,ymid,0); +/* fprintf(tfp, "PA%.4f,%.4f;\n", xmid, ymid);*/ + } + else { + quadratic_spline(x1, y1, ((x1+a2)/2.0), ((y1+b2)/2.0), + ((3.0*a2+a3)/4.0), ((3.0*b2+b3)/4.0), xmid, ymid); + } + + if ((fabs(xmid - x4) < THRESHOLD) && (fabs(ymid - y4) < THRESHOLD)) { + splinerel(x4,y4,0); +/* fprintf(tfp, "PA%.4f,%.4f;\n", x4, y4);*/ + } + else { + quadratic_spline(xmid, ymid, ((a2+3.0*a3)/4.0), ((b2+3.0*b3)/4.0), + ((a3+x4)/2.0), ((b3+y4)/2.0), x4, y4); + }; +}; /* quadratic_spline */ + +#define XCOORD(i) numbers[(2*i)] +#define YCOORD(i) numbers[(2*i)+1] +static void +vdmspline(int numpoints, int ox,int oy, int *numbers) +{ + double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; + double x1, y1, x2, y2; + char xcoord[4],ycoord[4]; + int i; + + /*p = s->points; + x1 = p->x/ppi;*/ + x1 = ox; + y1 = oy; +/* p = p->next; + x2 = p->x/ppi; + y2 = p->y/ppi;*/ + x2 = ox + XCOORD(0); + y2 = oy + YCOORD(0); + cx1 = (x1 + x2)/2.0; + cy1 = (y1 + y2)/2.0; + cx2 = (x1 + 3.0*x2)/4.0; + cy2 = (y1 + 3.0*y2)/4.0; + +/* fprintf(stderr,"Spline %d (%d,%d)\n",numpoints,(int)x1,(int)y1);*/ + vdmprintf("1%s%s",vdmnum((int)x1,xcoord),vdmnum((int)y1,ycoord)); + splinerel(x1,y1,-1); + splinerel(cx1,cy1,0); +/* fprintf(tfp, "PA%.4f,%.4f;PD%.4f,%.4f;\n", + x1, y1, cx1, cy1);*/ + + /*for (p = p->next; p != NULL; p = p->next) {*/ + for (i = 1; i < (numpoints); i++) { + x1 = x2; + y1 = y2; +/* x2 = p->x/ppi; + y2 = p->y/ppi;*/ + x2 = x1 + XCOORD(i); + y2 = y1 + YCOORD(i); + cx3 = (3.0*x1 + x2)/4.0; + cy3 = (3.0*y1 + y2)/4.0; + cx4 = (x1 + x2)/2.0; + cy4 = (y1 + y2)/2.0; + /* fprintf(stderr,"Point (%d,%d) - (%d,%d)\n",(int)x1,(int)(y1),(int)x2,(int)y2);*/ + quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4); + cx1 = cx4; + cy1 = cy4; + cx2 = (x1 + 3.0*x2)/4.0; + cy2 = (y1 + 3.0*y2)/4.0; + } + x1 = x2; + y1 = y2; +/* p = s->points->next; + x2 = p->x/ppi; + y2 = p->y/ppi;*/ + x2 = ox + XCOORD(0); + y2 = oy + YCOORD(0); + cx3 = (3.0*x1 + x2)/4.0; + cy3 = (3.0*y1 + y2)/4.0; + cx4 = (x1 + x2)/2.0; + cy4 = (y1 + y2)/2.0; + splinerel(x1,y1,0); + splinerel(x1,y1,1); + /*vdmprintf("%s%s",vdmnum((int)(x1-lx),xcoord),\ + vdmnum((int)(y1-ly),ycoord));*/ + vdmprintf("\x1e\n"); +/* fprintf(tfp, "PA%.4f,%.4f;PU;\n", x1, y1);*/ + + +}; /* vdmspline */ + + +#endif diff --git a/contrib/groff/src/devices/grolj4/Makefile.sub b/contrib/groff/src/devices/grolj4/Makefile.sub new file mode 100644 index 0000000..bbb0cff --- /dev/null +++ b/contrib/groff/src/devices/grolj4/Makefile.sub @@ -0,0 +1,6 @@ +PROG=grolj4 +MAN1=grolj4.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=lj4.o +CCSRCS=$(srcdir)/lj4.cc diff --git a/contrib/groff/src/devices/grolj4/grolj4.man b/contrib/groff/src/devices/grolj4/grolj4.man new file mode 100644 index 0000000..891d7dc --- /dev/null +++ b/contrib/groff/src/devices/grolj4/grolj4.man @@ -0,0 +1,144 @@ +.ig \"-*- nroff -*- +Copyright (C) 1994-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROLJ4 @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grolj4 \- groff driver for HP Laserjet 4 family +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fBgrolj4 'u +.ti \niu +.B grolj4 +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-lv +.OP \-d \fR[\fPn\fR]\fP +.OP \-c n +.OP \-p paper_size +.OP \-w n +.OP \-F dir +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +.B grolj4 +is a driver for +.B groff +that produces output in PCL5 format suitable for an HP Laserjet 4 printer. +.LP +There is an additional drawing command available: +.TP +.BI \eD'R\ dh\ dv ' +Draw a rule (solid black rectangle), with one corner +at the current position, and the diagonally opposite corner +at the current position +.RI +( dh , dv ). +Afterwards the current position will be at the opposite corner. This +generates a PCL fill rectangle command, and so will work on +printers that do not support HPGL/2 unlike the other +.B \eD +commands. +.SH OPTIONS +.TP +.BI \-c n +Print +.I n +copies of each page. +.TP +.B \-l +Print the document with a landscape orientation. +.TP +.BI "\-d [" n ] +Use duplex mode +.IR n : +1\ is long-side binding; 2\ is short-side binding; +default is\ 1. +.TP +.BI \-p size +Set the paper size to +.IR size , +which must be one of +letter, legal, executive, a4, com10, monarch, c5, b5, dl. +.TP +.B \-v +Print the version number. +.TP +.BI \-w n +Set the default line thickness to +.I n +thousandths of an em. +.TP +.BI \-F dir +Prepend directory +.IB dir /devlj4 +to the search path for font and device description files. +.LP +The following four commands are available additionally in the +.B DESC +file: +.TP +.BI pclweight \ N +The integer value +.I N +must be in the range -7 to +7; default is 0. +.TP +.BI pclstyle \ N +The integer value +.I N +must be in the range 0 to 32767; default is 0. +.TP +.BI pclproportional \ N +A boolean flag which can be either 0 or 1; default is 0. +.TP +.BI pcltypeface \ N +The integer value +.I N +must be in the range 0 to 65535; default is 0. +.SH FILES +.TP +.B @FONTDIR@/devlj4/DESC +Device description file. +.TP +.BI @FONTDIR@/devlj4/ F +Font description file for font +.IR F . +.TP +.B @MACRODIR@/lj4.tmac +Macros for use with +.BR grolj4 . +.SH BUGS +Small dots. +.SH "SEE ALSO" +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/src/devices/grolj4/lj4.cc b/contrib/groff/src/devices/grolj4/lj4.cc new file mode 100644 index 0000000..9fbc6af --- /dev/null +++ b/contrib/groff/src/devices/grolj4/lj4.cc @@ -0,0 +1,710 @@ +// -*- C++ -*- +/* Copyright (C) 1994, 2000, 2001 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* +TODO + +option to use beziers for circle/ellipse/arc +option to use lines for spline (for LJ3) +left/top offset registration +output bin selection option +paper source option +output non-integer parameters using fixed point numbers +X command to insert contents of file +X command to specify inline escape sequence (how to specify unprintable chars?) +X command to include bitmap graphics +*/ + +#include "driver.h" +#include "nonposix.h" + +static struct { + const char *name; + int code; + // at 300dpi + int x_offset_portrait; + int x_offset_landscape; +} paper_table[] = { + { "letter", 2, 75, 60 }, + { "legal", 3, 75, 60 }, + { "executive", 1, 75, 60 }, + { "a4", 26, 71, 59 }, + { "com10", 81, 75, 60 }, + { "monarch", 80, 75, 60 }, + { "c5", 91, 71, 59 }, + { "b5", 100, 71, 59 }, + { "dl", 90, 71, 59 }, +}; + +static int paper_size = -1; +static int landscape_flag = 0; +static int duplex_flag = 0; + +// An upper limit on the paper size in centipoints, +// used for setting HPGL picture frame. +#define MAX_PAPER_WIDTH (12*720) +#define MAX_PAPER_HEIGHT (17*720) + +// Dotted lines that are thinner than this don't work right. +#define MIN_DOT_PEN_WIDTH .351 + +#ifndef DEFAULT_LINE_WIDTH_FACTOR +// in ems/1000 +#define DEFAULT_LINE_WIDTH_FACTOR 40 +#endif + +const int DEFAULT_HPGL_UNITS = 1016; +int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR; +unsigned ncopies = 0; // 0 means don't send ncopies command + +class lj4_font : public font { +public: + ~lj4_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static lj4_font *load_lj4_font(const char *); + int weight; + int style; + int proportional; + int typeface; +private: + lj4_font(const char *); +}; + +lj4_font::lj4_font(const char *nm) +: font(nm), weight(0), style(0), proportional(0), typeface(0) +{ +} + +lj4_font::~lj4_font() +{ +} + +lj4_font *lj4_font::load_lj4_font(const char *s) +{ + lj4_font *f = new lj4_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +static struct { + const char *s; + int lj4_font::*ptr; + int min; + int max; +} command_table[] = { + { "pclweight", &lj4_font::weight, -7, 7 }, + { "pclstyle", &lj4_font::style, 0, 32767 }, + { "pclproportional", &lj4_font::proportional, 0, 1 }, + { "pcltypeface", &lj4_font::typeface, 0, 65535 }, +}; + +void lj4_font::handle_unknown_font_command(const char *command, + const char *arg, + const char *filename, int lineno) +{ + for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++) { + if (strcmp(command, command_table[i].s) == 0) { + if (arg == 0) + fatal_with_file_and_line(filename, lineno, + "`%1' command requires an argument", + command); + char *ptr; + long n = strtol(arg, &ptr, 10); + if (n == 0 && ptr == arg) + fatal_with_file_and_line(filename, lineno, + "`%1' command requires numeric argument", + command); + if (n < command_table[i].min) { + error_with_file_and_line(filename, lineno, + "argument for `%1' command must not be less than %2", + command, command_table[i].min); + n = command_table[i].min; + } + else if (n > command_table[i].max) { + error_with_file_and_line(filename, lineno, + "argument for `%1' command must not be greater than %2", + command, command_table[i].max); + n = command_table[i].max; + } + this->*command_table[i].ptr = int(n); + break; + } + } +} + +class lj4_printer : public printer { +public: + lj4_printer(); + ~lj4_printer(); + void set_char(int, font *, const environment *, int, const char *name); + void draw(int code, int *p, int np, const environment *env); + void begin_page(int); + void end_page(int page_length); + font *make_font(const char *); + void end_of_line(); +private: + void set_line_thickness(int size, int dot = 0); + void hpgl_init(); + void hpgl_start(); + void hpgl_end(); + int moveto(int hpos, int vpos); + int moveto1(int hpos, int vpos); + + int cur_hpos; + int cur_vpos; + lj4_font *cur_font; + int cur_size; + unsigned short cur_symbol_set; + int x_offset; + int line_thickness; + double pen_width; + double hpgl_scale; + int hpgl_inited; +}; + +inline +int lj4_printer::moveto(int hpos, int vpos) +{ + if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0) + return moveto1(hpos, vpos); + else + return 1; +} + +inline +void lj4_printer::hpgl_start() +{ + fputs("\033%1B", stdout); +} + +inline +void lj4_printer::hpgl_end() +{ + fputs(";\033%0A", stdout); +} + +lj4_printer::lj4_printer() +: cur_hpos(-1), + cur_font(0), + cur_size(0), + cur_symbol_set(0), + line_thickness(-1), + pen_width(-1.0), + hpgl_inited(0) +{ + if (7200 % font::res != 0) + fatal("invalid resolution %1: resolution must be a factor of 7200", + font::res); + fputs("\033E", stdout); // reset + if (font::res != 300) + printf("\033&u%dD", font::res); // unit of measure + if (ncopies > 0) + printf("\033&l%uX", ncopies); + if (paper_size < 0) + paper_size = 0; // default to letter + printf("\033&l%dA" // paper size + "\033&l%dO" // orientation + "\033&l0E", // no top margin + paper_table[paper_size].code, + landscape_flag != 0); + if (landscape_flag) + x_offset = paper_table[paper_size].x_offset_landscape; + else + x_offset = paper_table[paper_size].x_offset_portrait; + x_offset = (x_offset * font::res) / 300; + if (duplex_flag) + printf("\033&l%dS", duplex_flag); +} + +lj4_printer::~lj4_printer() +{ + fputs("\033E", stdout); +} + +void lj4_printer::begin_page(int) +{ +} + +void lj4_printer::end_page(int) +{ + putchar('\f'); + cur_hpos = -1; +} + +void lj4_printer::end_of_line() +{ + cur_hpos = -1; // force absolute motion +} + +inline +int is_unprintable(unsigned char c) +{ + return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27); +} + +void lj4_printer::set_char(int index, font *f, const environment *env, int w, const char *name) +{ + int code = f->get_code(index); + + unsigned char ch = code & 0xff; + unsigned short symbol_set = code >> 8; + if (symbol_set != cur_symbol_set) { + printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64); + cur_symbol_set = symbol_set; + } + if (f != cur_font) { + lj4_font *psf = (lj4_font *)f; + // FIXME only output those that are needed + printf("\033(s%dp%ds%db%dT", + psf->proportional, + psf->style, + psf->weight, + psf->typeface); + if (!psf->proportional || !cur_font || !cur_font->proportional) + cur_size = 0; + cur_font = psf; + } + if (env->size != cur_size) { + if (cur_font->proportional) { + static const char *quarters[] = { "", ".25", ".5", ".75" }; + printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]); + } + else { + double pitch = double(font::res)/w; + // PCL uses the next largest pitch, so round it down. + pitch = floor(pitch*100.0)/100.0; + printf("\033(s%.2fH", pitch); + } + cur_size = env->size; + } + if (!moveto(env->hpos, env->vpos)) + return; + if (is_unprintable(ch)) + fputs("\033&p1X", stdout); + putchar(ch); + cur_hpos += w; +} + +int lj4_printer::moveto1(int hpos, int vpos) +{ + if (hpos < x_offset || vpos < 0) + return 0; + fputs("\033*p", stdout); + if (cur_hpos < 0) + printf("%dx%dY", hpos - x_offset, vpos); + else { + if (cur_hpos != hpos) + printf("%s%d%c", hpos > cur_hpos ? "+" : "", + hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x'); + if (cur_vpos != vpos) + printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos); + } + cur_hpos = hpos; + cur_vpos = vpos; + return 1; +} + +void lj4_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + case 'R': + { + if (np != 2) { + error("2 arguments required for rule"); + break; + } + int hpos = env->hpos; + int vpos = env->vpos; + int hsize = p[0]; + int vsize = p[1]; + if (hsize < 0) { + hpos += hsize; + hsize = -hsize; + } + if (vsize < 0) { + vpos += vsize; + vsize = -vsize; + } + if (!moveto(hpos, vpos)) + return; + printf("\033*c%da%db0P", hsize, vsize); + break; + } + case 'l': + if (np != 2) { + error("2 arguments required for line"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size, p[0] == 0 && p[1] == 0); + printf("PD%d,%d", p[0], p[1]); + hpgl_end(); + break; + case 'p': + case 'P': + { + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + if (code == 'p') + set_line_thickness(env->size); + printf("PMPD%d", p[0]); + for (int i = 1; i < np; i++) + printf(",%d", p[i]); + printf("PM2%cP", code == 'p' ? 'E' : 'F'); + hpgl_end(); + break; + } + case '~': + { + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size); + printf("PD%d,%d", p[0]/2, p[1]/2); + const int tnum = 2; + const int tden = 3; + if (np > 2) { + fputs("BR", stdout); + for (int i = 0; i < np - 2; i += 2) { + if (i != 0) + putchar(','); + printf("%d,%d,%d,%d,%d,%d", + (p[i]*tnum)/(2*tden), + (p[i + 1]*tnum)/(2*tden), + p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden), + p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden), + (p[i] - p[i]/2) + p[i + 2]/2, + (p[i + 1] - p[i + 1]/2) + p[i + 3]/2); + } + } + printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2); + hpgl_end(); + break; + } + case 'c': + case 'C': + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + hpgl_init(); + if (!moveto(env->hpos + p[0]/2, env->vpos)) + return; + hpgl_start(); + if (code == 'c') { + set_line_thickness(env->size); + printf("CI%d", p[0]/2); + } + else + printf("WG%d,0,360", p[0]/2); + hpgl_end(); + break; + case 'e': + case 'E': + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + hpgl_init(); + if (!moveto(env->hpos + p[0]/2, env->vpos)) + return; + hpgl_start(); + printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale); + if (code == 'e') { + set_line_thickness(env->size); + printf("CI%d", p[1]/2); + } + else + printf("WG%d,0,360", p[1]/2); + printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale); + hpgl_end(); + break; + case 'a': + { + if (np != 4) { + error("4 arguments required for arc"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size); + double c[2]; + if (adjust_arc_center(p, c)) { + double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]) + - atan2(-c[1], -c[0])) + * 180.0/PI); + if (sweep > 0.0) + sweep -= 360.0; + printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep); + } + else + printf("PD%d,%d", p[0] + p[2], p[1] + p[3]); + hpgl_end(); + } + break; + case 'f': + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + hpgl_init(); + hpgl_start(); + if (p[0] >= 0 && p[0] <= 1000) + printf("FT10,%d", p[0]/10); + hpgl_end(); + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } + else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } +} + +void lj4_printer::hpgl_init() +{ + if (hpgl_inited) + return; + hpgl_inited = 1; + hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res; + printf("\033&f0S" // push position + "\033*p0x0Y" // move to 0,0 + "\033*c%dx%dy0T" // establish picture frame + "\033%%1B" // switch to HPGL + "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling + "LA1,4,2,4" // round line ends and joins + "PR" // relative plotting + "TR0" // opaque + ";\033%%1A" // back to PCL + "\033&f1S", // pop position + MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT, + hpgl_scale, hpgl_scale); +} + +void lj4_printer::set_line_thickness(int size, int dot) +{ + double pw; + if (line_thickness < 0) + pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0); + else + pw = line_thickness*25.4/font::res; + if (dot && pw < MIN_DOT_PEN_WIDTH) + pw = MIN_DOT_PEN_WIDTH; + if (pw != pen_width) { + printf("PW%f", pw); + pen_width = pw; + } +} + +font *lj4_printer::make_font(const char *nm) +{ + return lj4_font::load_lj4_font(nm); +} + +printer *make_printer() +{ + return new lj4_printer; +} + +static +int lookup_paper_size(const char *s) +{ + for (int i = 0; i < sizeof(paper_table)/sizeof(paper_table[0]); i++) { + // FIXME Perhaps allow unique prefix. + if (strcasecmp(s, paper_table[i].name) == 0) + return i; + } + return -1; +} + +static +void handle_unknown_desc_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "papersize") == 0) { + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`papersize' command requires an argument"); + else if (paper_size < 0) { + int n = lookup_paper_size(arg); + if (n < 0) + error_with_file_and_line(filename, lineno, + "unknown paper size `%1'", arg); + else + paper_size = n; + } + } +} + +static void usage(FILE *stream); + +extern "C" int optopt, optind; + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + font::set_unknown_desc_command_handler(handle_unknown_desc_command); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, ":F:p:d:lvw:c:", long_options, NULL)) + != EOF) + switch(c) { + case 'l': + landscape_flag = 1; + break; + case ':': + if (optopt == 'd') { + fprintf(stderr, "duplex assumed to be long-side\n"); + duplex_flag = 1; + } else + fprintf(stderr, "option -%c requires an operand\n", optopt); + fflush(stderr); + break; + case 'd': + if (!isdigit(*optarg)) // this ugly hack prevents -d without + optind--; // args from messing up the arg list + duplex_flag = atoi(optarg); + if (duplex_flag != 1 && duplex_flag != 2) { + fprintf(stderr, "odd value for duplex; assumed to be long-side\n"); + duplex_flag = 1; + } + break; + case 'p': + { + int n = lookup_paper_size(optarg); + if (n < 0) + error("unknown paper size `%1'", optarg); + else + paper_size = n; + break; + } + case 'v': + { + extern const char *Version_string; + printf("GNU grolj4 (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'F': + font::command_line_font_dir(optarg); + break; + case 'c': + { + char *ptr; + long n = strtol(optarg, &ptr, 10); + if (n == 0 && ptr == optarg) + error("argument for -c must be a positive integer"); + else if (n <= 0 || n > 32767) + error("out of range argument for -c"); + else + ncopies = unsigned(n); + break; + } + case 'w': + { + char *ptr; + long n = strtol(optarg, &ptr, 10); + if (n == 0 && ptr == optarg) + error("argument for -w must be a non-negative integer"); + else if (n < 0 || n > INT_MAX) + error("out of range argument for -w"); + else + line_width_factor = int(n); + break; + } + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } +#ifdef SET_BINARY + SET_BINARY(fileno(stdout)); +#endif + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n" + " [-w n] [-F dir] [files ...]\n", + program_name); +} diff --git a/contrib/groff/src/devices/grops/Makefile.sub b/contrib/groff/src/devices/grops/Makefile.sub new file mode 100644 index 0000000..4182527 --- /dev/null +++ b/contrib/groff/src/devices/grops/Makefile.sub @@ -0,0 +1,12 @@ +PROG=grops +MAN1=grops.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=\ + ps.o \ + psrm.o +CCSRCS=\ + $(srcdir)/ps.cc \ + $(srcdir)/psrm.cc +HDRS=\ + $(srcdir)/ps.h diff --git a/contrib/groff/src/devices/grops/TODO b/contrib/groff/src/devices/grops/TODO new file mode 100644 index 0000000..da67973 --- /dev/null +++ b/contrib/groff/src/devices/grops/TODO @@ -0,0 +1,29 @@ +Read PFB files directly. + +Generate %%DocumentMedia comment. + +Generate %%For comment. + +Generate %%Title comment. + +For efficiency it might be better to have the printer interface have +support for the t and u commands. + +Angles in arc command: don't generate more digits after the decimal +point than are necessary. + +Possibly generate BoundingBox comment. + +Per font composite character mechanism (sufficient for fractions). + +Consider whether we ought to do rounding of graphical objects other +than lines. What's the point? + +Error messages should refer to output page number. + +Search for downloadable fonts using their PostScript names if not +found in download file. + +Separate path for searching for downloadable font files. + +Clip to the BoundingBox when importing documents. diff --git a/contrib/groff/src/devices/grops/grops.man b/contrib/groff/src/devices/grops/grops.man new file mode 100644 index 0000000..1dac725 --- /dev/null +++ b/contrib/groff/src/devices/grops/grops.man @@ -0,0 +1,861 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROPS @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grops \- PostScript driver for groff +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fBgrops 'u +.ti \niu +.B grops +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-glmv +.OP \-b n +.OP \-c n +.OP \-w n +.OP \-F dir +.OP \-P prologue +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +.B grops +translates the output of GNU +.B troff +to PostScript. +Normally +.B grops +should be invoked by using the groff command +with a +.B \-Tps +option. +.if '@DEVICE@'ps' (Actually, this is the default for groff.) +If no files are given, +.B grops +will read the standard input. +A filename of +.B \- +will also cause +.B grops +to read the standard input. +PostScript output is written to the standard output. +When +.B grops +is run by +.B groff +options can be passed to +.B grops +using the +.B groff +.B \-P +option. +.SH OPTIONS +.TP +.BI \-b n +Workaround broken spoolers and previewers. +Normally +.B grops +produces output that conforms +the Document Structuring Conventions version 3.0. +Unfortunately some spoolers and previewers can't handle such output. +The value of +.I n +controls what +.B grops +does to its output acceptable to such programs. +A value of 0 will cause grops not to employ any workarounds. +Add 1 if no +.B %%BeginDocumentSetup +and +.B %%EndDocumentSetup +comments should be generated; +this is needed for early versions of TranScript that get confused by +anything between the +.B %%EndProlog +comment and the first +.B %%Page +comment. +Add 2 if lines in included files beginning with +.B %! +should be stripped out; this is needed for Sun's pageview previewer. +Add 4 if +.BR %%Page , +.BR %%Trailer +and +.B %%EndProlog +comments should be +stripped out of included files; this is needed for spoolers that +don't understand the +.B %%BeginDocument +and +.B %%EndDocument +comments. +Add 8 if the first line of the PostScript output should be +.B %!PS-Adobe-2.0 +rather than +.BR %!PS-Adobe-3.0 ; +this is needed when using Sun's Newsprint with a printer that requires +page reversal. +The default value can be specified by a +.RS +.IP +.BI broken\ n +.LP +command in the DESC file. +Otherwise the default value is 0. +.RE +.TP +.BI \-c n +Print +.I n +copies of each page. +.TP +.BI \-g +Guess the page length. +This generates PostScript code that guesses the page length. +The guess will be correct only if the imageable area is vertically +centered on the page. +This option allows you to generate documents that can be printed +both on letter (8.5\(mu11) paper and on A4 paper without change. +.TP +.B \-l +Print the document in landscape format. +.TP +.B \-m +Turn manual feed on for the document. +.TP +.BI \-F dir +Prepend directory +.IB dir /dev name +to the search path for prologue, font, and device description files; +.I name +is the name of the device, usually +.BR ps . +.TP +.BI \-P prologue-file +Use the file +.I prologue-file +(in the font path) as the prologue instead of the default prologue file +.BR prologue . +This option overrides the environment variable +.SM GROPS_PROLOGUE. +.TP +.BI \-w n +Lines should be drawn using a thickness of +.I n +thousandths of an em. +.TP +.B \-v +Print the version number. +.SH USAGE +There are styles called +.BR R , +.BR I , +.BR B , +and +.B BI +mounted at font positions 1 to 4. +The fonts are grouped into families +.BR A , +.BR BM , +.BR C , +.BR H , +.BR HN , +.BR N , +.B P +and +.B T +having members in each of these styles: +.de FT +.if '\\*(.T'ps' .ft \\$1 +.. +.TP +.B AR +.FT AR +AvantGarde-Book +.FT +.TP +.B AI +.FT AI +AvantGarde-BookOblique +.FT +.TP +.B AB +.FT AB +AvantGarde-Demi +.FT +.TP +.B ABI +.FT ABI +AvantGarde-DemiOblique +.FT +.TP +.B BMR +.FT BMR +Bookman-Light +.FT +.TP +.B BMI +.FT BMI +Bookman-LightItalic +.FT +.TP +.B BMB +.FT BMB +Bookman-Demi +.FT +.TP +.B BMBI +.FT BMBI +Bookman-DemiItalic +.FT +.TP +.B CR +.FT CR +Courier +.FT +.TP +.B CI +.FT CI +Courier-Oblique +.FT +.TP +.B CB +.FT CB +Courier-Bold +.FT +.TP +.B CBI +.FT CBI +Courier-BoldOblique +.FT +.TP +.B HR +.FT HR +Helvetica +.FT +.TP +.B HI +.FT HI +Helvetica-Oblique +.FT +.TP +.B HB +.FT HB +Helvetica-Bold +.FT +.TP +.B HBI +.FT HBI +Helvetica-BoldOblique +.FT +.TP +.B HNR +.FT HNR +Helvetica-Narrow +.FT +.TP +.B HNI +.FT HNI +Helvetica-Narrow-Oblique +.FT +.TP +.B HNB +.FT HNB +Helvetica-Narrow-Bold +.FT +.TP +.B HNBI +.FT HNBI +Helvetica-Narrow-BoldOblique +.FT +.TP +.B NR +.FT NR +NewCenturySchlbk-Roman +.FT +.TP +.B NI +.FT NI +NewCenturySchlbk-Italic +.FT +.TP +.B NB +.FT NB +NewCenturySchlbk-Bold +.FT +.TP +.B NBI +.FT NBI +NewCenturySchlbk-BoldItalic +.FT +.TP +.B PR +.FT PR +Palatino-Roman +.FT +.TP +.B PI +.FT PI +Palatino-Italic +.FT +.TP +.B PB +.FT PB +Palatino-Bold +.FT +.TP +.B PBI +.FT PBI +Palatino-BoldItalic +.FT +.TP +.B TR +.FT TR +Times-Roman +.FT +.TP +.B TI +.FT TI +Times-Italic +.FT +.TP +.B TB +.FT TB +Times-Bold +.FT +.TP +.B TBI +.FT TBI +Times-BoldItalic +.FT +.LP +There is also the following font which is not a member of a family: +.TP +.B ZCMI +.FT ZCMI +ZapfChancery-MediumItalic +.FT +.LP +There are also some special fonts called +.B SS +and +.BR S . +Zapf Dingbats is available as +.BR ZD +and a reversed version of ZapfDingbats (with symbols pointing in the opposite +direction) is available as +.BR ZDR ; +most characters in these fonts are unnamed and must be accessed using +.BR \eN . +.LP +.B grops +understands various X commands produced using the +.B \eX +escape sequence; +.B grops +will only interpret commands that begin with a +.B ps: +tag. +.TP +.BI \eX'ps:\ exec\ code ' +This executes the arbitrary PostScript commands in +.IR code . +The PostScript currentpoint will be set to the position of the +.B \eX +command before executing +.IR code . +The origin will be at the top left corner of the page, +and y coordinates will increase down the page. +A procedure +.B u +will be defined that converts groff units +to the coordinate system in effect. +For example, +.RS +.IP +.B +\&.nr x 1i +.br +.B +\eX'ps: exec \enx u 0 rlineto stroke' +.br +.RE +.IP +will draw a horizontal line one inch long. +.I code +may make changes to the graphics state, +but any changes will persist only to the +end of the page. +A dictionary containing the definitions specified by the +.B def +and +.B mdef +will be on top of the dictionary stack. +If your code adds definitions to this dictionary, +you should allocate space for them using +.BI \eX'ps\ mdef \ n '\fR. +Any definitions will persist only until the end of the page. +If you use the +.B \eY +escape sequence with an argument that names a macro, +.I code +can extend over multiple lines. +For example, +.RS +.IP +.nf +.ft B +\&.nr x 1i +\&.de y +\&ps: exec +\&\enx u 0 rlineto +\&stroke +\&.. +\&\eYy +.fi +.ft R +.LP +is another way to draw a horizontal line one inch long. +.RE +.TP +.BI \eX'ps:\ file\ name ' +This is the same as the +.B exec +command except that the PostScript code is read from file +.IR name . +.TP +.BI \eX'ps:\ def\ code ' +Place a PostScript definition contained in +.I code +in the prologue. +There should be at most one definition per +.B \eX +command. +Long definitions can be split over several +.B \eX +commands; +all the +.I code +arguments are simply joined together separated by newlines. +The definitions are placed in a dictionary which is automatically +pushed on the dictionary stack when an +.B exec +command is executed. +If you use the +.B \eY +escape sequence with an argument that names a macro, +.I code +can extend over multiple lines. +.TP +.BI \eX'ps:\ mdef\ n\ code ' +Like +.BR def , +except that +.I code +may contain up to +.I n +definitions. +.B grops +needs to know how many definitions +.I code +contains +so that it can create an appropriately sized PostScript dictionary +to contain them. +.TP +.BI \eX'ps:\ import\ file\ llx\ lly\ urx\ ury\ width\ \fR[\fP\ height\ \fR]\fP ' +Import a PostScript graphic from +.IR file . +The arguments +.IR llx , +.IR lly , +.IR urx , +and +.I ury +give the bounding box of the graphic in the default PostScript +coordinate system; they should all be integers; +.I llx +and +.I lly +are the x and y coordinates of the lower left +corner of the graphic; +.I urx +and +.I ury +are the x and y coordinates of the upper right corner of the graphic; +.I width +and +.I height +are integers that give the desired width and height in groff +units of the graphic. +The graphic will be scaled so that it has this width and height +and translated so that the lower left corner of the graphic is +located at the position associated with +.B \eX +command. +If the height argument is omitted it will be scaled uniformly in the +x and y directions so that it has the specified width. +Note that the contents of the +.B \eX +command are not interpreted by +.BR troff ; +so vertical space for the graphic is not automatically added, +and the +.I width +and +.I height +arguments are not allowed to have attached scaling indicators. +If the PostScript file complies with the Adobe Document Structuring +Conventions and contains a +.B %%BoundingBox +comment, then the bounding box can be automatically +extracted from within groff by using the +.B psbb +request. +.RS +.LP +The +.B \-mps +macros (which are automatically loaded when +.B grops +is run by the groff command) include a +.B PSPIC +macro which allows a picture to be easily imported. +This has the format +.IP +\&\fB.PSPIC\fP [ \fB\-L\fP | \fB-R\fP | \fB\-I\fP \fIn\fP ]\ \" +\fI\|file\fP [ \fIwidth\fP [ \fIheight\fP ]] +.LP +.I file +is the name of the file containing the illustration; +.I width +and +.I height +give the desired width and height of the graphic. +The +.I width +and +.I height +arguments may have scaling indicators attached; +the default scaling indicator is +.BR i . +This macro will scale the graphic uniformly +in the x and y directions so that it is no more than +.I width +wide +and +.I height +high. +By default, the graphic will be horizontally centered. +The +.BI \-L +and +.BI \-R +cause the graphic to be left-aligned and right-aligned +respectively. +The +.B \-I +option causes the graphic to be indented by +.IR n . +.RE +.TP +.B \eX'ps:\ invis' +.br +.ns +.TP +.B \eX'ps:\ endinvis' +No output will be generated for text and drawing commands +that are bracketed with these +.B \eX +commands. +These commands are intended for use when output from +.B troff +will be previewed before being processed with +.BR grops ; +if the previewer is unable to display certain characters +or other constructs, then other substitute characters or constructs +can be used for previewing by bracketing them with these +.B \eX +commands. +.RS +.LP +For example, +.B gxditview +is not able to display a proper +.B \e(em +character because the standard X11 fonts do not provide it; +this problem can be overcome by executing the following +request +.IP +.ft B +.nf +\&.char \e(em \eX'ps: invis'\e +\eZ'\ev'-.25m'\eh'.05m'\eD'l .9m 0'\eh'.05m''\e +\eX'ps: endinvis'\e(em +.ft +.fi +.LP +In this case, +.B gxditview +will be unable to display the +.B \e(em +character and will draw the line, +whereas +.B grops +will print the +.B \e(em +character +and ignore the line. +.RE +.LP +The input to +.B grops +must be in the format output by +.BR @g@troff (@MAN1EXT@). +This is described in +.BR groff_out (@MAN5EXT@). +In addition the device and font description files for the device used +must meet certain requirements. +The device and font description files supplied for +.B ps +device meet all these requirements. +.BR afmtodit (@MAN1EXT@) +can be used to create font files from AFM files. +The resolution must be an integer multiple of 72 times the +.BR sizescale . +The +.B ps +device uses a resolution of 72000 and a sizescale of 1000. +The device description file should contain a command +.IP +.BI paperlength\ n +.LP +which says that output should be generated which is suitable for +printing on a page whose length is +.I n +machine units. +Each font description file must contain a command +.IP +.BI internalname\ psname +.LP +which says that the PostScript name of the font is +.IR psname . +It may also contain a command +.IP +.BI encoding\ enc_file +.LP +which says that +the PostScript font should be reencoded using the encoding described in +.IR enc_file ; +this file should consist of a sequence of lines of the form: +.IP +.I +pschar code +.LP +where +.I pschar +is the PostScript name of the character, +and +.I code +is its position in the encoding expressed as a decimal integer. +The code for each character given in the font file must correspond +to the code for the character in encoding file, or to the code in the default +encoding for the font if the PostScript font is not to be reencoded. +This code can be used with the +.B \eN +escape sequence in +.B troff +to select the character, +even if the character does not have a groff name. +Every character in the font file must exist in the PostScript font, and +the widths given in the font file must match the widths used +in the PostScript font. +.B grops +will assume that a character with a groff name of +.B space +is blank (makes no marks on the page); +it can make use of such a character to generate more efficient and +compact PostScript output. +.LP +.B grops +can automatically include the downloadable fonts necessary +to print the document. +Any downloadable fonts which should, when required, be included by +.B grops +must be listed in the file +.BR @FONTDIR@/devps/download ; +this should consist of lines of the form +.IP +.I +font filename +.LP +where +.I font +is the PostScript name of the font, +and +.I filename +is the name of the file containing the font; +lines beginning with +.B # +and blank lines are ignored; +fields may be separated by tabs or spaces; +.I filename +will be searched for using the same mechanism that is used +for groff font metric files. +The +.B download +file itself will also be searched for using this mechanism. +.LP +If the file containing a downloadable font or imported document +conforms to the Adobe Document Structuring Conventions, +then +.B grops +will interpret any comments in the files sufficiently to ensure that its +own output is conforming. +It will also supply any needed font resources that are listed in the +.B download +file +as well as any needed file resources. +It is also able to handle inter-resource dependencies. +For example, suppose that you have a downloadable font called Garamond, +and also a downloadable font called Garamond-Outline +which depends on Garamond +(typically it would be defined to copy Garamond's font dictionary, +and change the PaintType), +then it is necessary for Garamond to be appear before Garamond-Outline +in the PostScript document. +.B grops +will handle this automatically +provided that the downloadable font file for Garamond-Outline +indicates its dependence on Garamond by means of +the Document Structuring Conventions, +for example by beginning with the following lines +.IP +.B +%!PS-Adobe-3.0 Resource-Font +.br +.B +%%DocumentNeededResources: font Garamond +.br +.B +%%EndComments +.br +.B +%%IncludeResource: font Garamond +.LP +In this case both Garamond and Garamond-Outline would need to be listed +in the +.B download +file. +A downloadable font should not include its own name in a +.B %%DocumentSuppliedResources +comment. +.LP +.B grops +will not interpret +.B %%DocumentFonts +comments. +The +.BR %%DocumentNeededResources , +.BR %%DocumentSuppliedResources , +.BR %%IncludeResource , +.BR %%BeginResource +and +.BR %%EndResource +comments +(or possibly the old +.BR %%DocumentNeededFonts , +.BR %%DocumentSuppliedFonts , +.BR %%IncludeFont , +.BR %%BeginFont +and +.BR %%EndFont +comments) +should be used. +.SH ENVIRONMENT +.TP +.SM +.B GROPS_PROLOGUE +If this is set to +.IR foo , +then +.B grops +will use the file +.I foo +(in the font path) instead of the default prologue file +.BR prologue . +The option +.B \-P +overrides this environment variable. +.SH FILES +.Tp \w'\fB@FONTDIR@/devps/download'u+2n +.B @FONTDIR@/devps/DESC +Device description file. +.TP +.BI @FONTDIR@/devps/ F +Font description file for font +.IR F . +.TP +.B @FONTDIR@/devps/download +List of downloadable fonts. +.TP +.B @FONTDIR@/devps/text.enc +Encoding used for text fonts. +.TP +.B @MACRODIR@/ps.tmac +Macros for use with +.BR grops ; +automatically loaded by +.BR troffrc +.TP +.B @MACRODIR@/pspic.tmac +Definition of +.B PSPIC +macro, +automatically loaded by +.BR ps.tmac . +.TP +.B @MACRODIR@/psold.tmac +Macros to disable use of characters not present in older +PostScript printers (e.g. `eth' or `thorn'). +.TP +.BI /tmp/grops XXXXXX +Temporary file. +.SH "SEE ALSO" +.BR afmtodit (@MAN1EXT@), +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR psbb (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/src/devices/grops/ps.cc b/contrib/groff/src/devices/grops/ps.cc new file mode 100644 index 0000000..98b1a91 --- /dev/null +++ b/contrib/groff/src/devices/grops/ps.cc @@ -0,0 +1,1570 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" +#include "nonposix.h" + +#include "ps.h" +#include <time.h> + +#ifdef NEED_DECLARATION_PUTENV +extern "C" { + int putenv(const char *); +} +#endif /* NEED_DECLARATION_PUTENV */ + +static int landscape_flag = 0; +static int manual_feed_flag = 0; +static int ncopies = 1; +static int linewidth = -1; +// Non-zero means generate PostScript code that guesses the paper +// length using the imageable area. +static int guess_flag = 0; + +// Non-zero if -b was specified on the command line. +static int bflag = 0; +unsigned broken_flags = 0; + +#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */ +#define MAX_LINE_LENGTH 72 +#define FILL_MAX 1000 + +const char *const dict_name = "grops"; +const char *const defs_dict_name = "DEFS"; +const int DEFS_DICT_SPARE = 50; + +double degrees(double r) +{ + return r*180.0/PI; +} + +double radians(double d) +{ + return d*PI/180.0; +} + +inline double transform_fill(int fill) +{ + return 1 - fill/double(FILL_MAX); +} + +// This is used for testing whether a character should be output in the +// PostScript file using \nnn, so we really want the character to be +// less than 0200. + +inline int is_ascii(char c) +{ + return (unsigned char)c < 0200; +} + +ps_output::ps_output(FILE *f, int n) +: fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0) +{ +} + +ps_output &ps_output::set_file(FILE *f) +{ + fp = f; + col = 0; + return *this; +} + +ps_output &ps_output::copy_file(FILE *infp) +{ + int c; + while ((c = getc(infp)) != EOF) + putc(c, fp); + return *this; +} + +ps_output &ps_output::end_line() +{ + if (col != 0) { + putc('\n', fp); + col = 0; + need_space = 0; + } + return *this; +} + +ps_output &ps_output::special(const char *s) +{ + if (s == 0 || *s == '\0') + return *this; + if (col != 0) { + putc('\n', fp); + col = 0; + } + fputs(s, fp); + if (strchr(s, '\0')[-1] != '\n') + putc('\n', fp); + need_space = 0; + return *this; +} + +ps_output &ps_output::simple_comment(const char *s) +{ + if (col != 0) + putc('\n', fp); + putc('%', fp); + putc('%', fp); + fputs(s, fp); + putc('\n', fp); + col = 0; + need_space = 0; + return *this; +} + +ps_output &ps_output::begin_comment(const char *s) +{ + if (col != 0) + putc('\n', fp); + putc('%', fp); + putc('%', fp); + fputs(s, fp); + col = 2 + strlen(s); + return *this; +} + +ps_output &ps_output::end_comment() +{ + if (col != 0) { + putc('\n', fp); + col = 0; + } + need_space = 0; + return *this; +} + +ps_output &ps_output::comment_arg(const char *s) +{ + int len = strlen(s); + if (col + len + 1 > max_line_length) { + putc('\n', fp); + fputs("%%+", fp); + col = 3; + } + putc(' ', fp); + fputs(s, fp); + col += len + 1; + return *this; +} + +ps_output &ps_output::set_fixed_point(int n) +{ + assert(n >= 0 && n <= 10); + fixed_point = n; + return *this; +} + +ps_output &ps_output::put_delimiter(char c) +{ + if (col + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc(c, fp); + col++; + need_space = 0; + return *this; +} + +ps_output &ps_output::put_string(const char *s, int n) +{ + int len = 0; + int i; + for (i = 0; i < n; i++) { + char c = s[i]; + if (is_ascii(c) && csprint(c)) { + if (c == '(' || c == ')' || c == '\\') + len += 2; + else + len += 1; + } + else + len += 4; + } + if (len > n*2) { + if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) { + putc('\n', fp); + col = 0; + } + if (col + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('<', fp); + col++; + for (i = 0; i < n; i++) { + if (col + 2 > max_line_length) { + putc('\n', fp); + col = 0; + } + fprintf(fp, "%02x", s[i] & 0377); + col += 2; + } + putc('>', fp); + col++; + } + else { + if (col + len + 2 > max_line_length && len + 2 <= max_line_length) { + putc('\n', fp); + col = 0; + } + if (col + 2 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('(', fp); + col++; + for (i = 0; i < n; i++) { + char c = s[i]; + if (is_ascii(c) && csprint(c)) { + if (c == '(' || c == ')' || c == '\\') + len = 2; + else + len = 1; + } + else + len = 4; + if (col + len + 1 > max_line_length) { + putc('\\', fp); + putc('\n', fp); + col = 0; + } + switch (len) { + case 1: + putc(c, fp); + break; + case 2: + putc('\\', fp); + putc(c, fp); + break; + case 4: + fprintf(fp, "\\%03o", c & 0377); + break; + default: + assert(0); + } + col += len; + } + putc(')', fp); + col++; + } + need_space = 0; + return *this; +} + +ps_output &ps_output::put_number(int n) +{ + char buf[1 + INT_DIGITS + 1]; + sprintf(buf, "%d", n); + int len = strlen(buf); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(buf, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_fix_number(int i) +{ + const char *p = if_to_a(i, fixed_point); + int len = strlen(p); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(p, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_float(double d) +{ + char buf[128]; + sprintf(buf, "%.4f", d); + int len = strlen(buf); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(buf, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_symbol(const char *s) +{ + int len = strlen(s); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(s, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_literal_symbol(const char *s) +{ + int len = strlen(s); + if (col > 0 && col + len + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('/', fp); + fputs(s, fp); + col += len + 1; + need_space = 1; + return *this; +} + +class ps_font : public font { + ps_font(const char *); +public: + int encoding_index; + char *encoding; + char *reencoded_name; + ~ps_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static ps_font *load_ps_font(const char *); +}; + +ps_font *ps_font::load_ps_font(const char *s) +{ + ps_font *f = new ps_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +ps_font::ps_font(const char *nm) +: font(nm), encoding_index(-1), encoding(0), reencoded_name(0) +{ +} + +ps_font::~ps_font() +{ + a_delete encoding; + a_delete reencoded_name; +} + +void ps_font::handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "encoding") == 0) { + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`encoding' command requires an argument"); + else + encoding = strsave(arg); + } +} + +static void handle_unknown_desc_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "broken") == 0) { + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`broken' command requires an argument"); + else if (!bflag) + broken_flags = atoi(arg); + } +} + +struct style { + font *f; + int point_size; + int height; + int slant; + style(); + style(font *, int, int, int); + int operator==(const style &) const; + int operator!=(const style &) const; +}; + +style::style() : f(0) +{ +} + +style::style(font *p, int sz, int h, int sl) +: f(p), point_size(sz), height(h), slant(sl) +{ +} + +int style::operator==(const style &s) const +{ + return (f == s.f && point_size == s.point_size + && height == s.height && slant == s.slant); +} + +int style::operator!=(const style &s) const +{ + return !(*this == s); +} + +class ps_printer : public printer { + FILE *tempfp; + ps_output out; + int res; + int space_char_index; + int pages_output; + int paper_length; + int equalise_spaces; + enum { SBUF_SIZE = 256 }; + char sbuf[SBUF_SIZE]; + int sbuf_len; + int sbuf_start_hpos; + int sbuf_vpos; + int sbuf_end_hpos; + int sbuf_space_width; + int sbuf_space_count; + int sbuf_space_diff_count; + int sbuf_space_code; + int sbuf_kern; + style sbuf_style; + style output_style; + int output_hpos; + int output_vpos; + int output_draw_point_size; + int line_thickness; + int output_line_thickness; + int fill; + unsigned char output_space_code; + enum { MAX_DEFINED_STYLES = 50 }; + style defined_styles[MAX_DEFINED_STYLES]; + int ndefined_styles; + int next_encoding_index; + string defs; + int ndefs; + resource_manager rm; + int invis_count; + + void flush_sbuf(); + void set_style(const style &); + void set_space_code(unsigned char c); + int set_encoding_index(ps_font *); + void do_exec(char *, const environment *); + void do_import(char *, const environment *); + void do_def(char *, const environment *); + void do_mdef(char *, const environment *); + void do_file(char *, const environment *); + void do_invis(char *, const environment *); + void do_endinvis(char *, const environment *); + void set_line_thickness(const environment *); + void fill_path(); + void encode_fonts(); + void define_encoding(const char *, int); + void reencode_font(ps_font *); +public: + ps_printer(); + ~ps_printer(); + void set_char(int i, font *f, const environment *env, int w, const char *name); + void draw(int code, int *p, int np, const environment *env); + void begin_page(int); + void end_page(int); + void special(char *arg, const environment *env, char type); + font *make_font(const char *); + void end_of_line(); +}; + +ps_printer::ps_printer() +: out(0, MAX_LINE_LENGTH), + pages_output(0), + sbuf_len(0), + output_hpos(-1), + output_vpos(-1), + line_thickness(-1), + fill(FILL_MAX + 1), + ndefined_styles(0), + next_encoding_index(0), + ndefs(0), + invis_count(0) +{ + tempfp = xtmpfile(); + out.set_file(tempfp); + if (linewidth < 0) + linewidth = DEFAULT_LINEWIDTH; + if (font::hor != 1) + fatal("horizontal resolution must be 1"); + if (font::vert != 1) + fatal("vertical resolution must be 1"); + if (font::res % (font::sizescale*72) != 0) + fatal("res must be a multiple of 72*sizescale"); + int r = font::res; + int point = 0; + while (r % 10 == 0) { + r /= 10; + point++; + } + res = r; + out.set_fixed_point(point); + space_char_index = font::name_to_index("space"); + paper_length = font::paperlength; + if (paper_length == 0) + paper_length = 11*font::res; + equalise_spaces = font::res >= 72000; +} + +int ps_printer::set_encoding_index(ps_font *f) +{ + if (f->encoding_index >= 0) + return f->encoding_index; + for (font_pointer_list *p = font_list; p; p = p->next) + if (p->p != f) { + char *encoding = ((ps_font *)p->p)->encoding; + int encoding_index = ((ps_font *)p->p)->encoding_index; + if (encoding != 0 && encoding_index >= 0 + && strcmp(f->encoding, encoding) == 0) { + return f->encoding_index = encoding_index; + } + } + return f->encoding_index = next_encoding_index++; +} + +void ps_printer::set_char(int i, font *f, const environment *env, int w, const char *name) +{ + if (i == space_char_index || invis_count > 0) + return; + unsigned char code = f->get_code(i); + style sty(f, env->size, env->height, env->slant); + if (sty.slant != 0) { + if (sty.slant > 80 || sty.slant < -80) { + error("silly slant `%1' degrees", sty.slant); + sty.slant = 0; + } + } + if (sbuf_len > 0) { + if (sbuf_len < SBUF_SIZE + && sty == sbuf_style + && sbuf_vpos == env->vpos) { + if (sbuf_end_hpos == env->hpos) { + sbuf[sbuf_len++] = code; + sbuf_end_hpos += w + sbuf_kern; + return; + } + if (sbuf_len == 1 && sbuf_kern == 0) { + sbuf_kern = env->hpos - sbuf_end_hpos; + sbuf_end_hpos = env->hpos + sbuf_kern + w; + sbuf[sbuf_len++] = code; + return; + } + /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off + starting a new string. */ + if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos + && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) { + if (sbuf_space_code < 0) { + if (f->contains(space_char_index)) { + sbuf_space_code = f->get_code(space_char_index); + sbuf_space_width = env->hpos - sbuf_end_hpos; + sbuf_end_hpos = env->hpos + w + sbuf_kern; + sbuf[sbuf_len++] = sbuf_space_code; + sbuf[sbuf_len++] = code; + sbuf_space_count++; + return; + } + } + else { + int diff = env->hpos - sbuf_end_hpos - sbuf_space_width; + if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) { + sbuf_end_hpos = env->hpos + w + sbuf_kern; + sbuf[sbuf_len++] = sbuf_space_code; + sbuf[sbuf_len++] = code; + sbuf_space_count++; + if (diff == 1) + sbuf_space_diff_count++; + else if (diff == -1) + sbuf_space_diff_count--; + return; + } + } + } + } + flush_sbuf(); + } + sbuf_len = 1; + sbuf[0] = code; + sbuf_end_hpos = env->hpos + w; + sbuf_start_hpos = env->hpos; + sbuf_vpos = env->vpos; + sbuf_style = sty; + sbuf_space_code = -1; + sbuf_space_width = 0; + sbuf_space_count = sbuf_space_diff_count = 0; + sbuf_kern = 0; +} + +static char *make_encoding_name(int encoding_index) +{ + static char buf[3 + INT_DIGITS + 1]; + sprintf(buf, "ENC%d", encoding_index); + return buf; +} + +const char *const WS = " \t\n\r"; + +void ps_printer::define_encoding(const char *encoding, int encoding_index) +{ + char *vec[256]; + int i; + for (i = 0; i < 256; i++) + vec[i] = 0; + char *path; + FILE *fp = font::open_file(encoding, &path); + if (fp == 0) + fatal("can't open encoding file `%1'", encoding); + int lineno = 1; + char buf[256]; + while (fgets(buf, 512, fp) != 0) { + char *p = buf; + while (csspace(*p)) + p++; + if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) { + char *q = strtok(0, WS); + int n; + if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256) + fatal_with_file_and_line(path, lineno, "bad second field"); + vec[n] = new char[strlen(p) + 1]; + strcpy(vec[n], p); + } + lineno++; + } + a_delete path; + out.put_literal_symbol(make_encoding_name(encoding_index)); + out.put_delimiter('['); + for (i = 0; i < 256; i++) { + if (vec[i] == 0) + out.put_literal_symbol(".notdef"); + else { + out.put_literal_symbol(vec[i]); + a_delete vec[i]; + } + } + out.put_delimiter(']').put_symbol("def"); +} + +void ps_printer::reencode_font(ps_font *f) +{ + out.put_literal_symbol(f->reencoded_name) + .put_symbol(make_encoding_name(f->encoding_index)) + .put_literal_symbol(f->get_internal_name()) + .put_symbol("RE"); +} + +void ps_printer::encode_fonts() +{ + if (next_encoding_index == 0) + return; + char *done_encoding = new char[next_encoding_index]; + for (int i = 0; i < next_encoding_index; i++) + done_encoding[i] = 0; + for (font_pointer_list *f = font_list; f; f = f->next) { + int encoding_index = ((ps_font *)f->p)->encoding_index; + if (encoding_index >= 0) { + assert(encoding_index < next_encoding_index); + if (!done_encoding[encoding_index]) { + done_encoding[encoding_index] = 1; + define_encoding(((ps_font *)f->p)->encoding, encoding_index); + } + reencode_font((ps_font *)f->p); + } + } + a_delete done_encoding; +} + +void ps_printer::set_style(const style &sty) +{ + char buf[1 + INT_DIGITS + 1]; + for (int i = 0; i < ndefined_styles; i++) + if (sty == defined_styles[i]) { + sprintf(buf, "F%d", i); + out.put_symbol(buf); + return; + } + if (ndefined_styles >= MAX_DEFINED_STYLES) + ndefined_styles = 0; + sprintf(buf, "F%d", ndefined_styles); + out.put_literal_symbol(buf); + const char *psname = sty.f->get_internal_name(); + if (psname == 0) + fatal("no internalname specified for font `%1'", sty.f->get_name()); + char *encoding = ((ps_font *)sty.f)->encoding; + if (encoding != 0) { + char *s = ((ps_font *)sty.f)->reencoded_name; + if (s == 0) { + int ei = set_encoding_index((ps_font *)sty.f); + char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1]; + sprintf(tem, "%s@%d", psname, ei); + psname = tem; + ((ps_font *)sty.f)->reencoded_name = tem; + } + else + psname = s; + } + out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size); + if (sty.height != 0 || sty.slant != 0) { + int h = sty.height == 0 ? sty.point_size : sty.height; + h *= font::res/(72*font::sizescale); + int c = int(h*tan(radians(sty.slant)) + .5); + out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname) + .put_symbol("MF"); + } + else { + out.put_literal_symbol(psname).put_symbol("SF"); + } + defined_styles[ndefined_styles++] = sty; +} + +void ps_printer::set_space_code(unsigned char c) +{ + out.put_literal_symbol("SC").put_number(c).put_symbol("def"); +} + +void ps_printer::end_of_line() +{ + flush_sbuf(); + // this ensures that we do an absolute motion to the beginning of a line + output_vpos = output_hpos = -1; +} + +void ps_printer::flush_sbuf() +{ + enum { + NONE, + RELATIVE_H, + RELATIVE_V, + RELATIVE_HV, + ABSOLUTE + } motion = NONE; + int space_flag = 0; + if (sbuf_len == 0) + return; + if (output_style != sbuf_style) { + set_style(sbuf_style); + output_style = sbuf_style; + } + int extra_space = 0; + if (output_hpos < 0 || output_vpos < 0) + motion = ABSOLUTE; + else { + if (output_hpos != sbuf_start_hpos) + motion = RELATIVE_H; + if (output_vpos != sbuf_vpos) { + if (motion != NONE) + motion = RELATIVE_HV; + else + motion = RELATIVE_V; + } + } + if (sbuf_space_code >= 0) { + int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size); + if (w + sbuf_kern != sbuf_space_width) { + if (sbuf_space_code != output_space_code) { + set_space_code(sbuf_space_code); + output_space_code = sbuf_space_code; + } + space_flag = 1; + extra_space = sbuf_space_width - w - sbuf_kern; + if (sbuf_space_diff_count > sbuf_space_count/2) + extra_space++; + else if (sbuf_space_diff_count < -(sbuf_space_count/2)) + extra_space--; + } + } + if (space_flag) + out.put_fix_number(extra_space); + if (sbuf_kern != 0) + out.put_fix_number(sbuf_kern); + out.put_string(sbuf, sbuf_len); + char command_array[] = {'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T'}; + char sym[2]; + sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)]; + sym[1] = '\0'; + switch (motion) { + case NONE: + break; + case ABSOLUTE: + out.put_fix_number(sbuf_start_hpos) + .put_fix_number(sbuf_vpos); + break; + case RELATIVE_H: + out.put_fix_number(sbuf_start_hpos - output_hpos); + break; + case RELATIVE_V: + out.put_fix_number(sbuf_vpos - output_vpos); + break; + case RELATIVE_HV: + out.put_fix_number(sbuf_start_hpos - output_hpos) + .put_fix_number(sbuf_vpos - output_vpos); + break; + default: + assert(0); + } + out.put_symbol(sym); + output_hpos = sbuf_end_hpos; + output_vpos = sbuf_vpos; + sbuf_len = 0; +} + + +void ps_printer::set_line_thickness(const environment *env) +{ + if (line_thickness < 0) { + if (output_draw_point_size != env->size) { + // we ought to check for overflow here + int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000; + out.put_fix_number(lw).put_symbol("LW"); + output_draw_point_size = env->size; + output_line_thickness = -1; + } + } + else { + if (output_line_thickness != line_thickness) { + out.put_fix_number(line_thickness).put_symbol("LW"); + output_line_thickness = line_thickness; + output_draw_point_size = -1; + } + } +} + +void ps_printer::fill_path() +{ + if (fill > FILL_MAX) + out.put_symbol("BL"); + else + out.put_float(transform_fill(fill)).put_symbol("FL"); +} + +void ps_printer::draw(int code, int *p, int np, const environment *env) +{ + if (invis_count > 0) + return; + int fill_flag = 0; + switch (code) { + case 'C': + fill_flag = 1; + // fall through + case 'c': + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + out.put_fix_number(env->hpos + p[0]/2) + .put_fix_number(env->vpos) + .put_fix_number(p[0]/2) + .put_symbol("DC"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'l': + if (np != 2) { + error("2 arguments required for line"); + break; + } + set_line_thickness(env); + out.put_fix_number(p[0] + env->hpos) + .put_fix_number(p[1] + env->vpos) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("DL"); + break; + case 'E': + fill_flag = 1; + // fall through + case 'e': + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + out.put_fix_number(p[0]) + .put_fix_number(p[1]) + .put_fix_number(env->hpos + p[0]/2) + .put_fix_number(env->vpos) + .put_symbol("DE"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'P': + fill_flag = 1; + // fall through + case 'p': + { + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("MT"); + for (int i = 0; i < np; i += 2) + out.put_fix_number(p[i]) + .put_fix_number(p[i+1]) + .put_symbol("RL"); + out.put_symbol("CL"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + } + case '~': + { + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("MT"); + out.put_fix_number(p[0]/2) + .put_fix_number(p[1]/2) + .put_symbol("RL"); + /* tnum/tden should be between 0 and 1; the closer it is to 1 + the tighter the curve will be to the guiding lines; 2/3 + is the standard value */ + const int tnum = 2; + const int tden = 3; + for (int i = 0; i < np - 2; i += 2) { + out.put_fix_number((p[i]*tnum)/(2*tden)) + .put_fix_number((p[i + 1]*tnum)/(2*tden)) + .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden)) + .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden)) + .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2) + .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2) + .put_symbol("RC"); + } + out.put_fix_number(p[np - 2] - p[np - 2]/2) + .put_fix_number(p[np - 1] - p[np - 1]/2) + .put_symbol("RL"); + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'a': + { + if (np != 4) { + error("4 arguments required for arc"); + break; + } + set_line_thickness(env); + double c[2]; + if (adjust_arc_center(p, c)) + out.put_fix_number(env->hpos + int(c[0])) + .put_fix_number(env->vpos + int(c[1])) + .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1]))) + .put_float(degrees(atan2(-c[1], -c[0]))) + .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]))) + .put_symbol("DA"); + else + out.put_fix_number(p[0] + p[2] + env->hpos) + .put_fix_number(p[1] + p[3] + env->vpos) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("DL"); + } + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } + else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + case 'f': + { + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) { + // This means fill with the current color. + fill = FILL_MAX + 1; + } + break; + } + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } + + output_hpos = output_vpos = -1; +} + + +void ps_printer::begin_page(int n) +{ + out.begin_comment("Page:").comment_arg(i_to_a(n)); + out.comment_arg(i_to_a(++pages_output)).end_comment(); + output_style.f = 0; + output_space_code = 32; + output_draw_point_size = -1; + output_line_thickness = -1; + output_hpos = output_vpos = -1; + ndefined_styles = 0; + out.simple_comment("BeginPageSetup"); + out.put_symbol("BP"); + out.simple_comment("EndPageSetup"); +} + +void ps_printer::end_page(int) +{ + flush_sbuf(); + out.put_symbol("EP"); + if (invis_count != 0) { + error("missing `endinvis' command"); + invis_count = 0; + } +} + +font *ps_printer::make_font(const char *nm) +{ + return ps_font::load_ps_font(nm); +} + +ps_printer::~ps_printer() +{ + out.simple_comment("Trailer"); + out.put_symbol("end"); + out.simple_comment("EOF"); + if (fseek(tempfp, 0L, 0) < 0) + fatal("fseek on temporary file failed"); + fputs("%!PS-Adobe-", stdout); + fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout); + putchar('\n'); + out.set_file(stdout); + { + extern const char *Version_string; + out.begin_comment("Creator:") + .comment_arg("groff") + .comment_arg("version") + .comment_arg(Version_string) + .end_comment(); + } + { + fputs("%%CreationDate: ", out.get_file()); +#ifdef LONG_FOR_TIME_T + long +#else + time_t +#endif + t = time(0); + fputs(ctime(&t), out.get_file()); + } + for (font_pointer_list *f = font_list; f; f = f->next) { + ps_font *psf = (ps_font *)(f->p); + rm.need_font(psf->get_internal_name()); + } + rm.print_header_comments(out); + out.begin_comment("Pages:").comment_arg(i_to_a(pages_output)).end_comment(); + out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment(); +#if 0 + fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n", + font::paperwidth*72.0/font::res, + paper_length*72.0/font::res); +#endif + out.begin_comment("Orientation:") + .comment_arg(landscape_flag ? "Landscape" : "Portrait") + .end_comment(); + if (ncopies != 1) { + out.end_line(); + fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies); + } + out.simple_comment("EndComments"); + out.simple_comment("BeginProlog"); + rm.output_prolog(out); + if (!(broken_flags & NO_SETUP_SECTION)) { + out.simple_comment("EndProlog"); + out.simple_comment("BeginSetup"); + } + rm.document_setup(out); + out.put_symbol(dict_name).put_symbol("begin"); + if (ndefs > 0) + ndefs += DEFS_DICT_SPARE; + out.put_literal_symbol(defs_dict_name) + .put_number(ndefs + 1) + .put_symbol("dict") + .put_symbol("def"); + out.put_symbol(defs_dict_name) + .put_symbol("begin"); + out.put_literal_symbol("u") + .put_delimiter('{') + .put_fix_number(1) + .put_symbol("mul") + .put_delimiter('}') + .put_symbol("bind") + .put_symbol("def"); + defs += '\0'; + out.special(defs.contents()); + out.put_symbol("end"); + if (ncopies != 1) + out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def"); + out.put_literal_symbol("RES").put_number(res).put_symbol("def"); + out.put_literal_symbol("PL"); + if (guess_flag) + out.put_symbol("PLG"); + else + out.put_fix_number(paper_length); + out.put_symbol("def"); + out.put_literal_symbol("LS") + .put_symbol(landscape_flag ? "true" : "false") + .put_symbol("def"); + if (manual_feed_flag) { + out.begin_comment("BeginFeature:") + .comment_arg("*ManualFeed") + .comment_arg("True") + .end_comment() + .put_symbol("MANUAL") + .simple_comment("EndFeature"); + } + encode_fonts(); + out.simple_comment((broken_flags & NO_SETUP_SECTION) + ? "EndProlog" + : "EndSetup"); + out.end_line(); + out.copy_file(tempfp); + fclose(tempfp); +} + +void ps_printer::special(char *arg, const environment *env, char type) +{ + if (type != 'p') + return; + typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *); + static struct { + const char *name; + SPECIAL_PROCP proc; + } proc_table[] = { + { "exec", &ps_printer::do_exec }, + { "def", &ps_printer::do_def }, + { "mdef", &ps_printer::do_mdef }, + { "import", &ps_printer::do_import }, + { "file", &ps_printer::do_file }, + { "invis", &ps_printer::do_invis }, + { "endinvis", &ps_printer::do_endinvis }, + }; + char *p; + for (p = arg; *p == ' ' || *p == '\n'; p++) + ; + char *tag = p; + for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++) + ; + if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) { + error("X command without `ps:' tag ignored"); + return; + } + p++; + for (; *p == ' ' || *p == '\n'; p++) + ; + char *command = p; + for (; *p != '\0' && *p != ' ' && *p != '\n'; p++) + ; + if (*command == '\0') { + error("X command without `ps:' tag ignored"); + return; + } + for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++) + if (strncmp(command, proc_table[i].name, p - command) == 0) { + (this->*(proc_table[i].proc))(p, env); + return; + } + error("X command `%1' not recognised", command); +} + +// A conforming PostScript document must not have lines longer +// than 255 characters (excluding line termination characters). + +static int check_line_lengths(const char *p) +{ + for (;;) { + const char *end = strchr(p, '\n'); + if (end == 0) + end = strchr(p, '\0'); + if (end - p > 255) + return 0; + if (*end == '\0') + break; + p = end + 1; + } + return 1; +} + +void ps_printer::do_exec(char *arg, const environment *env) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (*arg == '\0') { + error("missing argument to X exec command"); + return; + } + if (!check_line_lengths(arg)) { + error("lines in X exec command must not be more than 255 characters long"); + return; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("EBEGIN") + .special(arg) + .put_symbol("EEND"); + output_hpos = output_vpos = -1; + output_style.f = 0; + output_draw_point_size = -1; + output_line_thickness = -1; + ndefined_styles = 0; + if (!ndefs) + ndefs = 1; +} + +void ps_printer::do_file(char *arg, const environment *env) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (*arg == '\0') { + error("missing argument to X file command"); + return; + } + const char *filename = arg; + do { + ++arg; + } while (*arg != '\0' && *arg != ' ' && *arg != '\n'); + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("EBEGIN"); + rm.import_file(filename, out); + out.put_symbol("EEND"); + output_hpos = output_vpos = -1; + output_style.f = 0; + output_draw_point_size = -1; + output_line_thickness = -1; + ndefined_styles = 0; + if (!ndefs) + ndefs = 1; +} + +void ps_printer::do_def(char *arg, const environment *) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (!check_line_lengths(arg)) { + error("lines in X def command must not be more than 255 characters long"); + return; + } + defs += arg; + if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') + defs += '\n'; + ndefs++; +} + +// Like def, but the first argument says how many definitions it contains. + +void ps_printer::do_mdef(char *arg, const environment *) +{ + flush_sbuf(); + char *p; + int n = (int)strtol(arg, &p, 10); + if (n == 0 && p == arg) { + error("first argument to X mdef must be an integer"); + return; + } + if (n < 0) { + error("out of range argument `%1' to X mdef command", int(n)); + return; + } + arg = p; + while (csspace(*arg)) + arg++; + if (!check_line_lengths(arg)) { + error("lines in X mdef command must not be more than 255 characters long"); + return; + } + defs += arg; + if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') + defs += '\n'; + ndefs += n; +} + +void ps_printer::do_import(char *arg, const environment *env) +{ + flush_sbuf(); + while (*arg == ' ' || *arg == '\n') + arg++; + char *p; + for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++) + ; + if (*p != '\0') + *p++ = '\0'; + int parms[6]; + int nparms = 0; + while (nparms < 6) { + char *end; + long n = strtol(p, &end, 10); + if (n == 0 && end == p) + break; + parms[nparms++] = int(n); + p = end; + } + if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) { + error("scaling indicators not allowed in arguments for X import command"); + return; + } + while (*p == ' ' || *p == '\n') + p++; + if (nparms < 5) { + if (*p == '\0') + error("too few arguments for X import command"); + else + error("invalid argument `%1' for X import command", p); + return; + } + if (*p != '\0') { + error("superflous argument `%1' for X import command", p); + return; + } + int llx = parms[0]; + int lly = parms[1]; + int urx = parms[2]; + int ury = parms[3]; + int desired_width = parms[4]; + int desired_height = parms[5]; + if (desired_width <= 0) { + error("bad width argument `%1' for X import command: must be > 0", + desired_width); + return; + } + if (nparms == 6 && desired_height <= 0) { + error("bad height argument `%1' for X import command: must be > 0", + desired_height); + return; + } + if (llx == urx) { + error("llx and urx arguments for X import command must not be equal"); + return; + } + if (lly == ury) { + error("lly and ury arguments for X import command must not be equal"); + return; + } + if (nparms == 5) { + int old_wid = urx - llx; + int old_ht = ury - lly; + if (old_wid < 0) + old_wid = -old_wid; + if (old_ht < 0) + old_ht = -old_ht; + desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5); + } + if (env->vpos - desired_height < 0) + warning("top of imported graphic is above the top of the page"); + out.put_number(llx) + .put_number(lly) + .put_fix_number(desired_width) + .put_number(urx - llx) + .put_fix_number(-desired_height) + .put_number(ury - lly) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("PBEGIN"); + rm.import_file(arg, out); + // do this here just in case application defines PEND + out.put_symbol("end"); + out.put_symbol("PEND"); +} + +void ps_printer::do_invis(char *, const environment *) +{ + invis_count++; +} + +void ps_printer::do_endinvis(char *, const environment *) +{ + if (invis_count == 0) + error("unbalanced `endinvis' command"); + else + --invis_count; +} + +printer *make_printer() +{ + return new ps_printer; +} + +static void usage(FILE *stream); + +int main(int argc, char **argv) +{ + program_name = argv[0]; + string env; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "F:P:glmc:w:vb:", long_options, NULL)) + != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + printf("GNU grops (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'c': + if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) { + error("bad number of copies `%s'", optarg); + ncopies = 1; + } + break; + case 'g': + guess_flag = 1; + break; + case 'l': + landscape_flag = 1; + break; + case 'm': + manual_feed_flag = 1; + break; + case 'F': + font::command_line_font_dir(optarg); + break; + case 'P': + env = "GROPS_PROLOGUE"; + env += '='; + env += optarg; + env += '\0'; + if (putenv(strsave(env.contents()))) + fatal("putenv failed"); + break; + case 'w': + if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) { + error("bad linewidth `%1'", optarg); + linewidth = -1; + } + break; + case 'b': + // XXX check this + broken_flags = atoi(optarg); + bflag = 1; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + font::set_unknown_desc_command_handler(handle_unknown_desc_command); +#ifdef SET_BINARY + SET_BINARY(fileno(stdout)); +#endif + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [-glmv] [-b n] [-c n] [-w n] [-P prologue] [-F dir] [files ...]\n", + program_name); +} diff --git a/contrib/groff/src/devices/grops/ps.h b/contrib/groff/src/devices/grops/ps.h new file mode 100644 index 0000000..6e78597 --- /dev/null +++ b/contrib/groff/src/devices/grops/ps.h @@ -0,0 +1,122 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +class ps_output { +public: + ps_output(FILE *, int max_line_length); + ps_output &put_string(const char *, int); + ps_output &put_number(int); + ps_output &put_fix_number(int); + ps_output &put_float(double); + ps_output &put_symbol(const char *); + ps_output &put_literal_symbol(const char *); + ps_output &set_fixed_point(int); + ps_output &simple_comment(const char *); + ps_output &begin_comment(const char *); + ps_output &comment_arg(const char *); + ps_output &end_comment(); + ps_output &set_file(FILE *); + ps_output &include_file(FILE *); + ps_output ©_file(FILE *); + ps_output &end_line(); + ps_output &put_delimiter(char); + ps_output &special(const char *); + FILE *get_file(); +private: + FILE *fp; + int col; + int max_line_length; // not including newline + int need_space; + int fixed_point; +}; + +inline FILE *ps_output::get_file() +{ + return fp; +} + +enum resource_type { + RESOURCE_FONT, + RESOURCE_PROCSET, + RESOURCE_FILE, + RESOURCE_ENCODING, + RESOURCE_FORM, + RESOURCE_PATTERN + }; + +struct resource; + +extern string an_empty_string; + +class resource_manager { +public: + resource_manager(); + ~resource_manager(); + void import_file(const char *filename, ps_output &); + void need_font(const char *name); + void print_header_comments(ps_output &); + void document_setup(ps_output &); + void output_prolog(ps_output &); +private: + unsigned extensions; + unsigned language_level; + resource *procset_resource; + resource *resource_list; + resource *lookup_resource(resource_type type, string &name, + string &version = an_empty_string, + unsigned revision = 0); + resource *lookup_font(const char *name); + void read_download_file(); + void supply_resource(resource *r, int rank, FILE *outfp, + int is_document = 0); + void process_file(int rank, FILE *fp, const char *filename, FILE *outfp); + resource *read_file_arg(const char **); + resource *read_procset_arg(const char **); + resource *read_font_arg(const char **); + resource *read_resource_arg(const char **); + void print_resources_comment(unsigned flag, FILE *outfp); + void print_extensions_comment(FILE *outfp); + void print_language_level_comment(FILE *outfp); + int do_begin_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_document(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_document(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_procset(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_procset(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_font(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_font(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_file(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_file(const char *ptr, int rank, FILE *fp, FILE *outfp); + int change_to_end_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_preview(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_data(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_binary(const char *ptr, int rank, FILE *fp, FILE *outfp); +}; + +extern unsigned broken_flags; + +// broken_flags is ored from these + +enum { + NO_SETUP_SECTION = 01, + STRIP_PERCENT_BANG = 02, + STRIP_STRUCTURE_COMMENTS = 04, + USE_PS_ADOBE_2_0 = 010 +}; diff --git a/contrib/groff/src/devices/grops/psfig.diff b/contrib/groff/src/devices/grops/psfig.diff new file mode 100644 index 0000000..5be080d --- /dev/null +++ b/contrib/groff/src/devices/grops/psfig.diff @@ -0,0 +1,106 @@ +These are patches to makes psfig work with groff. They apply to the +version of psfig in comp.sources.unix/Volume11. After applying them, +psfig should be recompiled with -DGROFF. The resulting psfig will +work only with groff, so you might want to install it under a +different name. The output of this psfig must be processed using the +macros in the file ../tmac/tmac.psfig. These will automatically add +the necessary PostScript code to the prologue output by grops. Use of +the `global' feature in psfig will result in non-conformant PostScript +which will fail if processed by a page reversal program. Note that +psfig is unsupported by me (I'm not interested in hearing about psfig +problems.) For new documents, I recommend using the PostScript +inclusion features provided by grops. + +James Clark +jjc@jclark.com + +*** cmds.c.~1~ Thu Feb 14 16:09:45 1991 +--- cmds.c Mon Mar 4 12:49:26 1991 +*************** +*** 245,253 **** +--- 245,261 ---- + (void) sprintf(x, "%.2fp", fx); + (void) sprintf(y, "%.2fp", fy); + } else if (!*x) { ++ #ifndef GROFF + (void) sprintf(x,"(%.2fp*%s/%.2fp)", fx, y, fy); ++ #else /* GROFF */ ++ (void) sprintf(x,"(%.0fu*%s/%.0fu)", fx, y, fy); ++ #endif /* GROFF */ + } else if (!*y) { ++ #ifndef GROFF + (void) sprintf(y,"(%.2fp*%s/%.2fp)", fy, x, fx); ++ #else /* GROFF */ ++ (void) sprintf(y,"(%.0fu*%s/%.0fu)", fy, x, fx); ++ #endif /* GROFF */ + } + + /* +*** troff.c.~1~ Thu Feb 14 16:09:48 1991 +--- troff.c Mon Mar 4 12:48:46 1991 +*************** +*** 26,32 **** +--- 26,36 ---- + } + + ++ #ifndef GROFF + char incl_file_s[] = "\\X'f%s'"; ++ #else /* GROFF */ ++ char incl_file_s[] = "\\X'ps: file %s'"; ++ #endif /* GROFF */ + includeFile(filenm) + char *filenm; { + printf(incl_file_s, filenm); +*************** +*** 40,52 **** +--- 44,64 ---- + error("buffer overflow"); + } + ++ #ifndef GROFF + char endfig_s[] = "\\X'pendFig'"; ++ #else /* GROFF */ ++ char endfig_s[] = "\\X'ps: exec psfigend'"; ++ #endif /* GROFF */ + endfig() { + printf(endfig_s); + } + + char startfig_s[] = ++ #ifndef GROFF + "\\X'p\\w@\\h@%s@@'\\X'p\\w@\\h@%s@@'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'pstartFig'"; ++ #else /* GROFF */ ++ "\\X'ps: exec \\w@\\h@%s@@ \\w@\\h@%s@@ %.2f %.2f %.2f %.2f psfigstart'"; ++ #endif /* GROFF */ + + startfig(x, y, llx, lly, urx, ury) + char *x, *y; +*************** +*** 57,63 **** +--- 69,79 ---- + } + + emitDoClip() { ++ #ifndef GROFF + printf("\\X'pdoclip'"); ++ #else /* GROFF */ ++ printf("\\X'ps: exec psfigclip'"); ++ #endif /* GROFF */ + } + + flushX() +*************** +*** 116,122 **** +--- 132,142 ---- + + #define isWhite(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n') + ++ #ifndef GROFF + char literal_s[] = "\\X'p%s'"; ++ #else /* GROFF */ ++ char literal_s[] = "\\X'ps: exec %s'"; ++ #endif /* GROFF */ + emitLiteral(text) + char *text; { + static char litbuf[BUFSZ]; diff --git a/contrib/groff/src/devices/grops/psrm.cc b/contrib/groff/src/devices/grops/psrm.cc new file mode 100644 index 0000000..b816c6b --- /dev/null +++ b/contrib/groff/src/devices/grops/psrm.cc @@ -0,0 +1,1118 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#include "ps.h" + +#ifdef NEED_DECLARATION_PUTENV +extern "C" { + int putenv(const char *); +} +#endif /* NEED_DECLARATION_PUTENV */ + +#define GROPS_PROLOGUE "prologue" + +static void print_ps_string(const string &s, FILE *outfp); + +cset white_space("\n\r \t"); +string an_empty_string; + +const char *extension_table[] = { + "DPS", + "CMYK", + "Composite", + "FileSystem", +}; + +const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]); + +const char *resource_table[] = { + "font", + "procset", + "file", + "encoding", + "form", + "pattern", +}; + +const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]); + +static int read_uint_arg(const char **pp, unsigned *res) +{ + while (white_space(**pp)) + *pp += 1; + if (**pp == '\0') { + error("missing argument"); + return 0; + } + const char *start = *pp; + // XXX use strtoul + long n = strtol(start, (char **)pp, 10); + if (n == 0 && *pp == start) { + error("not an integer"); + return 0; + } + if (n < 0) { + error("argument must not be negative"); + return 0; + } + *res = unsigned(n); + return 1; +} + +struct resource { + resource *next; + resource_type type; + string name; + enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 }; + unsigned flags; + string version; + unsigned revision; + char *filename; + int rank; + resource(resource_type, string &, string & = an_empty_string, unsigned = 0); + ~resource(); + void print_type_and_name(FILE *outfp); +}; + +resource::resource(resource_type t, string &n, string &v, unsigned r) +: next(0), type(t), flags(0), revision(r), filename(0), rank(-1) +{ + name.move(n); + version.move(v); + if (type == RESOURCE_FILE) { + if (name.search('\0') >= 0) + error("filename contains a character with code 0"); + filename = name.extract(); + } +} + +resource::~resource() +{ + a_delete filename; +} + +void resource::print_type_and_name(FILE *outfp) +{ + fputs(resource_table[type], outfp); + putc(' ', outfp); + print_ps_string(name, outfp); + if (type == RESOURCE_PROCSET) { + putc(' ', outfp); + print_ps_string(version, outfp); + fprintf(outfp, " %u", revision); + } +} + +resource_manager::resource_manager() +: extensions(0), language_level(0), resource_list(0) +{ + read_download_file(); + string procset_name("grops"); + extern const char *version_string; + extern const char *revision_string; + unsigned revision_uint; + if ( !read_uint_arg( &revision_string, &revision_uint) ) + revision_uint = 0; + string procset_version(version_string); + procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name, + procset_version, revision_uint); + procset_resource->flags |= resource::SUPPLIED; +} + +resource_manager::~resource_manager() +{ + while (resource_list) { + resource *tem = resource_list; + resource_list = resource_list->next; + delete tem; + } +} + +resource *resource_manager::lookup_resource(resource_type type, + string &name, + string &version, + unsigned revision) +{ + resource *r; + for (r = resource_list; r; r = r->next) + if (r->type == type + && r->name == name + && r->version == version + && r->revision == revision) + return r; + r = new resource(type, name, version, revision); + r->next = resource_list; + resource_list = r; + return r; +} + +// Just a specialized version of lookup_resource(). + +resource *resource_manager::lookup_font(const char *name) +{ + resource *r; + for (r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT + && strlen(name) == r->name.length() + && memcmp(name, r->name.contents(), r->name.length()) == 0) + return r; + string s(name); + r = new resource(RESOURCE_FONT, s); + r->next = resource_list; + resource_list = r; + return r; +} + +void resource_manager::need_font(const char *name) +{ + lookup_font(name)->flags |= resource::FONT_NEEDED; +} + +typedef resource *Presource; // Work around g++ bug. + +void resource_manager::document_setup(ps_output &out) +{ + int nranks = 0; + resource *r; + for (r = resource_list; r; r = r->next) + if (r->rank >= nranks) + nranks = r->rank + 1; + if (nranks > 0) { + // Sort resource_list in reverse order of rank. + Presource *head = new Presource[nranks + 1]; + Presource **tail = new Presource *[nranks + 1]; + int i; + for (i = 0; i < nranks + 1; i++) { + head[i] = 0; + tail[i] = &head[i]; + } + for (r = resource_list; r; r = r->next) { + i = r->rank < 0 ? 0 : r->rank + 1; + *tail[i] = r; + tail[i] = &(*tail[i])->next; + } + resource_list = 0; + for (i = 0; i < nranks + 1; i++) + if (head[i]) { + *tail[i] = resource_list; + resource_list = head[i]; + } + a_delete head; + a_delete tail; + // check it + for (r = resource_list; r; r = r->next) + if (r->next) + assert(r->rank >= r->next->rank); + for (r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT && r->rank >= 0) + supply_resource(r, -1, out.get_file()); + } +} + +void resource_manager::print_resources_comment(unsigned flag, FILE *outfp) +{ + int continued = 0; + for (resource *r = resource_list; r; r = r->next) + if (r->flags & flag) { + if (continued) + fputs("%%+ ", outfp); + else { + fputs(flag == resource::NEEDED + ? "%%DocumentNeededResources: " + : "%%DocumentSuppliedResources: ", + outfp); + continued = 1; + } + r->print_type_and_name(outfp); + putc('\n', outfp); + } +} + +void resource_manager::print_header_comments(ps_output &out) +{ + for (resource *r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED)) + supply_resource(r, 0, 0); + print_resources_comment(resource::NEEDED, out.get_file()); + print_resources_comment(resource::SUPPLIED, out.get_file()); + print_language_level_comment(out.get_file()); + print_extensions_comment(out.get_file()); +} + +void resource_manager::output_prolog(ps_output &out) +{ + FILE *outfp = out.get_file(); + out.end_line(); + char *path; + if (!getenv("GROPS_PROLOGUE")) { + string e = "GROPS_PROLOGUE"; + e += '='; + e += GROPS_PROLOGUE; + e += '\0'; + if (putenv(strsave(e.contents()))) + fatal("putenv failed"); + } + char *prologue = getenv("GROPS_PROLOGUE"); + FILE *fp = font::open_file(prologue, &path); + if (!fp) + fatal("can't find `%1'", prologue); + fputs("%%BeginResource: ", outfp); + procset_resource->print_type_and_name(outfp); + putc('\n', outfp); + process_file(-1, fp, path, outfp); + fclose(fp); + a_delete path; + fputs("%%EndResource\n", outfp); +} + +void resource_manager::import_file(const char *filename, ps_output &out) +{ + out.end_line(); + string name(filename); + resource *r = lookup_resource(RESOURCE_FILE, name); + supply_resource(r, -1, out.get_file(), 1); +} + +void resource_manager::supply_resource(resource *r, int rank, FILE *outfp, + int is_document) +{ + if (r->flags & resource::BUSY) { + r->name += '\0'; + fatal("loop detected in dependency graph for %1 `%2'", + resource_table[r->type], + r->name.contents()); + } + r->flags |= resource::BUSY; + if (rank > r->rank) + r->rank = rank; + char *path; + FILE *fp = 0; + if (r->filename != 0) { + if (r->type == RESOURCE_FONT) { + fp = font::open_file(r->filename, &path); + if (!fp) { + error("can't find `%1'", r->filename); + a_delete r->filename; + r->filename = 0; + } + } + else { + errno = 0; + fp = fopen(r->filename, "r"); + if (!fp) { + error("can't open `%1': %2", r->filename, strerror(errno)); + a_delete r->filename; + r->filename = 0; + } + else + path = r->filename; + } + } + if (fp) { + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) { + fputs("%%BeginDocument: ", outfp); + print_ps_string(r->name, outfp); + putc('\n', outfp); + } + else { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + process_file(rank, fp, path, outfp); + fclose(fp); + if (r->type == RESOURCE_FONT) + a_delete path; + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) + fputs("%%EndDocument\n", outfp); + else + fputs("%%EndResource\n", outfp); + } + r->flags |= resource::SUPPLIED; + } + else { + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) { + fputs("%%IncludeDocument: ", outfp); + print_ps_string(r->name, outfp); + putc('\n', outfp); + } + else { + fputs("%%IncludeResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + r->flags |= resource::NEEDED; + } + r->flags &= ~resource::BUSY; +} + + +#define PS_LINE_MAX 255 +#define PS_MAGIC "%!PS-Adobe-" + +static int ps_get_line(char *buf, FILE *fp) +{ + int c = getc(fp); + if (c == EOF) { + buf[0] = '\0'; + return 0; + } + current_lineno++; + int i = 0; + int err = 0; + while (c != '\r' && c != '\n' && c != EOF) { + if ((c < 0x1b && !white_space(c)) || c == 0x7f) + error("illegal input character code %1", int(c)); + else if (i < PS_LINE_MAX) + buf[i++] = c; + else if (!err) { + err = 1; + error("PostScript file non-conforming " + "because length of line exceeds 255"); + } + c = getc(fp); + } + buf[i++] = '\n'; + buf[i] = '\0'; + if (c == '\r') { + c = getc(fp); + if (c != EOF && c != '\n') + ungetc(c, fp); + } + return 1; +} + +static int read_text_arg(const char **pp, string &res) +{ + res.clear(); + while (white_space(**pp)) + *pp += 1; + if (**pp == '\0') { + error("missing argument"); + return 0; + } + if (**pp != '(') { + for (; **pp != '\0' && !white_space(**pp); *pp += 1) + res += **pp; + return 1; + } + *pp += 1; + res.clear(); + int level = 0; + for (;;) { + if (**pp == '\0' || **pp == '\r' || **pp == '\n') { + error("missing ')'"); + return 0; + } + if (**pp == ')') { + if (level == 0) { + *pp += 1; + break; + } + res += **pp; + level--; + } + else if (**pp == '(') { + level++; + res += **pp; + } + else if (**pp == '\\') { + *pp += 1; + switch (**pp) { + case 'n': + res += '\n'; + break; + case 'r': + res += '\n'; + break; + case 't': + res += '\t'; + break; + case 'b': + res += '\b'; + break; + case 'f': + res += '\f'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val = **pp - '0'; + if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { + *pp += 1; + val = val*8 + (**pp - '0'); + if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { + *pp += 1; + val = val*8 + (**pp - '0'); + } + } + } + break; + default: + res += **pp; + break; + } + } + else + res += **pp; + *pp += 1; + } + return 1; +} + +resource *resource_manager::read_file_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(RESOURCE_FILE, arg); +} + +resource *resource_manager::read_font_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(RESOURCE_FONT, arg); +} + +resource *resource_manager::read_procset_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + string version; + if (!read_text_arg(ptr, version)) + return 0; + unsigned revision; + if (!read_uint_arg(ptr, &revision)) + return 0; + return lookup_resource(RESOURCE_PROCSET, arg, version, revision); +} + +resource *resource_manager::read_resource_arg(const char **ptr) +{ + while (white_space(**ptr)) + *ptr += 1; + const char *name = *ptr; + while (**ptr != '\0' && !white_space(**ptr)) + *ptr += 1; + if (name == *ptr) { + error("missing resource type"); + return 0; + } + int ri; + for (ri = 0; ri < NRESOURCES; ri++) + if (strlen(resource_table[ri]) == *ptr - name + && memcmp(resource_table[ri], name, *ptr - name) == 0) + break; + if (ri >= NRESOURCES) { + error("unknown resource type"); + return 0; + } + if (ri == RESOURCE_PROCSET) + return read_procset_arg(ptr); + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(resource_type(ri), arg); +} + +static const char *matches_comment(const char *buf, const char *comment) +{ + if (buf[0] != '%' || buf[1] != '%') + return 0; + for (buf += 2; *comment; comment++, buf++) + if (*buf != *comment) + return 0; + if (comment[-1] == ':') + return buf; + if (*buf == '\0' || white_space(*buf)) + return buf; + return 0; +} + +// Return 1 if the line should be copied out. + +int resource_manager::do_begin_resource(const char *ptr, int, FILE *, + FILE *) +{ + resource *r = read_resource_arg(&ptr); + if (r) + r->flags |= resource::SUPPLIED; + return 1; +} + +int resource_manager::do_include_resource(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_resource_arg(&ptr); + if (r) { + if (r->type == RESOURCE_FONT) { + if (rank >= 0) + supply_resource(r, rank + 1, outfp); + else + r->flags |= resource::FONT_NEEDED; + } + else + supply_resource(r, rank, outfp); + } + return 0; +} + +int resource_manager::do_begin_document(const char *ptr, int, FILE *, + FILE *) +{ + resource *r = read_file_arg(&ptr); + if (r) + r->flags |= resource::SUPPLIED; + return 1; +} + +int resource_manager::do_include_document(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) + supply_resource(r, rank, outfp, 1); + return 0; +} + +int resource_manager::do_begin_procset(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_procset_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_procset(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_procset_arg(&ptr); + if (r) + supply_resource(r, rank, outfp); + return 0; +} + +int resource_manager::do_begin_file(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_file(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) + supply_resource(r, rank, outfp); + return 0; +} + +int resource_manager::do_begin_font(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_font_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_font(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_font_arg(&ptr); + if (r) { + if (rank >= 0) + supply_resource(r, rank + 1, outfp); + else + r->flags |= resource::FONT_NEEDED; + } + return 0; +} + +int resource_manager::change_to_end_resource(const char *, int, FILE *, + FILE *outfp) +{ + if (outfp) + fputs("%%EndResource\n", outfp); + return 0; +} + +int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *) +{ + char buf[PS_LINE_MAX + 2]; + do { + if (!ps_get_line(buf, fp)) { + error("end of file in preview section"); + break; + } + } while (!matches_comment(buf, "EndPreview")); + return 0; +} + +int read_one_of(const char **ptr, const char **s, int n) +{ + while (white_space(**ptr)) + *ptr += 1; + if (**ptr == '\0') + return -1; + const char *start = *ptr; + do { + ++(*ptr); + } while (**ptr != '\0' && !white_space(**ptr)); + for (int i = 0; i < n; i++) + if (strlen(s[i]) == *ptr - start + && memcmp(s[i], start, *ptr - start) == 0) + return i; + return -1; +} + +int resource_manager::do_begin_data(const char *ptr, int, FILE *fp, + FILE *outfp) +{ + while (white_space(*ptr)) + ptr++; + const char *start = ptr; + unsigned numberof; + if (!read_uint_arg(&ptr, &numberof)) + return 0; + static const char *types[] = { "Binary", "Hex", "ASCII" }; + const int Binary = 0; + int type = 0; + static const char *units[] = { "Bytes", "Lines" }; + const int Bytes = 0; + int unit = Bytes; + while (white_space(*ptr)) + ptr++; + if (*ptr != '\0') { + type = read_one_of(&ptr, types, 3); + if (type < 0) { + error("bad data type"); + return 0; + } + while (white_space(*ptr)) + ptr++; + if (*ptr != '\0') { + unit = read_one_of(&ptr, units, 2); + if (unit < 0) { + error("expected `Bytes' or `Lines'"); + return 0; + } + } + } + if (type != Binary) + return 1; + if (outfp) { + fputs("%%BeginData: ", outfp); + fputs(start, outfp); + } + if (numberof > 0) { + unsigned bytecount = 0; + unsigned linecount = 0; + do { + int c = getc(fp); + if (c == EOF) { + error("end of file within data section"); + return 0; + } + if (outfp) + putc(c, outfp); + bytecount++; + if (c == '\r') { + int cc = getc(fp); + if (cc != '\n') { + linecount++; + current_lineno++; + } + if (cc != EOF) + ungetc(c, fp); + } + else if (c == '\n') { + linecount++; + current_lineno++; + } + } while ((unit == Bytes ? bytecount : linecount) < numberof); + } + char buf[PS_LINE_MAX + 2]; + if (!ps_get_line(buf, fp)) { + error("missing %%%%EndData line"); + return 0; + } + if (!matches_comment(buf, "EndData")) + error("bad %%%%EndData line"); + if (outfp) + fputs(buf, outfp); + return 0; +} + +int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp, + FILE *outfp) +{ + if (!outfp) + return 0; + unsigned count; + if (!read_uint_arg(&ptr, &count)) + return 0; + if (outfp) + fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count); + while (count != 0) { + int c = getc(fp); + if (c == EOF) { + error("end of file within binary section"); + return 0; + } + if (outfp) + putc(c, outfp); + --count; + if (c == '\r') { + int cc = getc(fp); + if (cc != '\n') + current_lineno++; + if (cc != EOF) + ungetc(c, fp); + } + else if (c == '\n') + current_lineno++; + } + char buf[PS_LINE_MAX + 2]; + if (!ps_get_line(buf, fp)) { + error("missing %%%%EndBinary line"); + return 0; + } + if (!matches_comment(buf, "EndBinary")) { + error("bad %%%%EndBinary line"); + if (outfp) + fputs(buf, outfp); + } + else if (outfp) + fputs("%%EndData\n", outfp); + return 0; +} + +static unsigned parse_extensions(const char *ptr) +{ + unsigned flags = 0; + for (;;) { + while (white_space(*ptr)) + ptr++; + if (*ptr == '\0') + break; + const char *name = ptr; + do { + ++ptr; + } while (*ptr != '\0' && !white_space(*ptr)); + int i; + for (i = 0; i < NEXTENSIONS; i++) + if (strlen(extension_table[i]) == ptr - name + && memcmp(extension_table[i], name, ptr - name) == 0) { + flags |= (1 << i); + break; + } + if (i >= NEXTENSIONS) { + string s(name, ptr - name); + s += '\0'; + error("unknown extension `%1'", s.contents()); + } + } + return flags; +} + +// XXX if it has not been surrounded with {Begin,End}Document need to strip +// out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections. + +// XXX Perhaps the decision whether to use BeginDocument or +// BeginResource: file should be postponed till we have seen +// the first line of the file. + +void resource_manager::process_file(int rank, FILE *fp, const char *filename, + FILE *outfp) +{ + // If none of these comments appear in the header section, and we are + // just analyzing the file (ie outfp is 0), then we can return immediately. + static const char *header_comment_table[] = { + "DocumentNeededResources:", + "DocumentSuppliedResources:", + "DocumentNeededFonts:", + "DocumentSuppliedFonts:", + "DocumentNeededProcSets:", + "DocumentSuppliedProcSets:", + "DocumentNeededFiles:", + "DocumentSuppliedFiles:", + }; + + const int NHEADER_COMMENTS = (sizeof(header_comment_table) + / sizeof(header_comment_table[0])); + struct comment_info { + const char *name; + int (resource_manager::*proc)(const char *, int, FILE *, FILE *); + }; + + static comment_info comment_table[] = { + { "BeginResource:", &resource_manager::do_begin_resource }, + { "IncludeResource:", &resource_manager::do_include_resource }, + { "BeginDocument:", &resource_manager::do_begin_document }, + { "IncludeDocument:", &resource_manager::do_include_document }, + { "BeginProcSet:", &resource_manager::do_begin_procset }, + { "IncludeProcSet:", &resource_manager::do_include_procset }, + { "BeginFont:", &resource_manager::do_begin_font }, + { "IncludeFont:", &resource_manager::do_include_font }, + { "BeginFile:", &resource_manager::do_begin_file }, + { "IncludeFile:", &resource_manager::do_include_file }, + { "EndProcSet", &resource_manager::change_to_end_resource }, + { "EndFont", &resource_manager::change_to_end_resource }, + { "EndFile", &resource_manager::change_to_end_resource }, + { "BeginPreview:", &resource_manager::do_begin_preview }, + { "BeginData:", &resource_manager::do_begin_data }, + { "BeginBinary:", &resource_manager::do_begin_binary }, + }; + + const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]); + char buf[PS_LINE_MAX + 2]; + int saved_lineno = current_lineno; + const char *saved_filename = current_filename; + current_filename = filename; + current_lineno = 0; + if (!ps_get_line(buf, fp)) { + current_filename = saved_filename; + current_lineno = saved_lineno; + return; + } + if (strlen(buf) < sizeof(PS_MAGIC) - 1 + || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) { + if (outfp) { + do { + if (!(broken_flags & STRIP_PERCENT_BANG) + || buf[0] != '%' || buf[1] != '!') + fputs(buf, outfp); + } while (ps_get_line(buf, fp)); + } + } + else { + if (!(broken_flags & STRIP_PERCENT_BANG) && outfp) + fputs(buf, outfp); + int in_header = 1; + int interesting = 0; + int had_extensions_comment = 0; + int had_language_level_comment = 0; + for (;;) { + if (!ps_get_line(buf, fp)) + break; + int copy_this_line = 1; + if (buf[0] == '%') { + if (buf[1] == '%') { + const char *ptr; + int i; + for (i = 0; i < NCOMMENTS; i++) + if ((ptr = matches_comment(buf, comment_table[i].name))) { + copy_this_line + = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp); + break; + } + if (i >= NCOMMENTS && in_header) { + if ((ptr = matches_comment(buf, "EndComments"))) + in_header = 0; + else if (!had_extensions_comment + && (ptr = matches_comment(buf, "Extensions:"))) { + extensions |= parse_extensions(ptr); + // XXX handle possibility that next line is %%+ + had_extensions_comment = 1; + } + else if (!had_language_level_comment + && (ptr = matches_comment(buf, "LanguageLevel:"))) { + unsigned ll; + if (read_uint_arg(&ptr, &ll) && ll > language_level) + language_level = ll; + had_language_level_comment = 1; + } + else { + for (i = 0; i < NHEADER_COMMENTS; i++) + if (matches_comment(buf, header_comment_table[i])) { + interesting = 1; + break; + } + } + } + if ((broken_flags & STRIP_STRUCTURE_COMMENTS) + && (matches_comment(buf, "EndProlog") + || matches_comment(buf, "Page:") + || matches_comment(buf, "Trailer"))) + copy_this_line = 0; + } + else if (buf[1] == '!') { + if (broken_flags & STRIP_PERCENT_BANG) + copy_this_line = 0; + } + } + else + in_header = 0; + if (!outfp && !in_header && !interesting) + break; + if (copy_this_line && outfp) + fputs(buf, outfp); + } + } + current_filename = saved_filename; + current_lineno = saved_lineno; +} + +void resource_manager::read_download_file() +{ + char *path = 0; + FILE *fp = font::open_file("download", &path); + if (!fp) + fatal("can't find `download'"); + char buf[512]; + int lineno = 0; + while (fgets(buf, sizeof(buf), fp)) { + lineno++; + char *p = strtok(buf, " \t\r\n"); + if (p == 0 || *p == '#') + continue; + char *q = strtok(0, " \t\r\n"); + if (!q) + fatal_with_file_and_line(path, lineno, "missing filename"); + lookup_font(p)->filename = strsave(q); + } + a_delete path; + fclose(fp); +} + +// XXX Can we share some code with ps_output::put_string()? + +static void print_ps_string(const string &s, FILE *outfp) +{ + int len = s.length(); + const char *str = s.contents(); + int funny = 0; + if (str[0] == '(') + funny = 1; + else { + for (int i = 0; i < len; i++) + if (str[i] <= 040 || str[i] > 0176) { + funny = 1; + break; + } + } + if (!funny) { + put_string(s, outfp); + return; + } + int level = 0; + int i; + for (i = 0; i < len; i++) + if (str[i] == '(') + level++; + else if (str[i] == ')' && --level < 0) + break; + putc('(', outfp); + for (i = 0; i < len; i++) + switch (str[i]) { + case '(': + case ')': + if (level != 0) + putc('\\', outfp); + putc(str[i], outfp); + break; + case '\\': + fputs("\\\\", outfp); + break; + case '\n': + fputs("\\n", outfp); + break; + case '\r': + fputs("\\r", outfp); + break; + case '\t': + fputs("\\t", outfp); + break; + case '\b': + fputs("\\b", outfp); + break; + case '\f': + fputs("\\f", outfp); + break; + default: + if (str[i] < 040 || str[i] > 0176) + fprintf(outfp, "\\%03o", str[i] & 0377); + else + putc(str[i], outfp); + break; + } + putc(')', outfp); +} + +void resource_manager::print_extensions_comment(FILE *outfp) +{ + if (extensions) { + fputs("%%Extensions:", outfp); + for (int i = 0; i < NEXTENSIONS; i++) + if (extensions & (1 << i)) { + putc(' ', outfp); + fputs(extension_table[i], outfp); + } + putc('\n', outfp); + } +} + +void resource_manager::print_language_level_comment(FILE *outfp) +{ + if (language_level) + fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level); +} + diff --git a/contrib/groff/src/devices/grotty/Makefile.sub b/contrib/groff/src/devices/grotty/Makefile.sub new file mode 100644 index 0000000..91d3908 --- /dev/null +++ b/contrib/groff/src/devices/grotty/Makefile.sub @@ -0,0 +1,6 @@ +PROG=grotty +MAN1=grotty.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=tty.o +CCSRCS=$(srcdir)/tty.cc diff --git a/contrib/groff/src/devices/grotty/TODO b/contrib/groff/src/devices/grotty/TODO new file mode 100644 index 0000000..3f23dc3 --- /dev/null +++ b/contrib/groff/src/devices/grotty/TODO @@ -0,0 +1,3 @@ +Document font and device description file usage of grotty. + +With -h avoid using a tab when a single space will do. diff --git a/contrib/groff/src/devices/grotty/grotty.man b/contrib/groff/src/devices/grotty/grotty.man index 51c6a49..8fdca02 100644 --- a/contrib/groff/src/devices/grotty/grotty.man +++ b/contrib/groff/src/devices/grotty/grotty.man @@ -39,7 +39,7 @@ translates the output of GNU into a form suitable for typewriter-like devices. Normally .B grotty -should invoked by using the +should be invoked by using the .B groff command with a @@ -120,9 +120,9 @@ escape sequence in .SH OPTIONS .TP .BI \-F dir -Search the directory +Prepend directory .IB dir /dev name -for font and device description files; +to the search path for font and device description files; .I name is the name of the device, usually .BR ascii , @@ -224,12 +224,12 @@ of .B cp1047 device. .TP -.B @MACRODIR@/tmac.tty +.B @MACRODIR@/tty.tmac Macros for use with .BR grotty . .TP -.B @MACRODIR@/tmac.tty-char -Additional klugey character definitions for use with +.B @MACRODIR@/tty-char.tmac +Additional klugdey character definitions for use with .BR grotty . .LP Note that on EBCDIC hosts, only files for the diff --git a/contrib/groff/src/devices/grotty/tty.cc b/contrib/groff/src/devices/grotty/tty.cc new file mode 100644 index 0000000..a8ee065 --- /dev/null +++ b/contrib/groff/src/devices/grotty/tty.cc @@ -0,0 +1,509 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2000, 2001 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 2, 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 groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "device.h" + +#ifndef SHRT_MIN +#define SHRT_MIN (-32768) +#endif + +#ifndef SHRT_MAX +#define SHRT_MAX 32767 +#endif + +#define TAB_WIDTH 8 + +static int horizontal_tab_flag = 0; +static int form_feed_flag = 0; +static int bold_flag = 1; +static int underline_flag = 1; +static int overstrike_flag = 1; +static int draw_flag = 1; + +enum { + UNDERLINE_MODE = 0x01, + BOLD_MODE = 0x02, + VDRAW_MODE = 0x04, + HDRAW_MODE = 0x08, + CU_MODE = 0x10 +}; + +// Mode to use for bold-underlining. +static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE; + +class tty_font : public font { + tty_font(const char *); + unsigned char mode; +public: + ~tty_font(); + unsigned char get_mode() { return mode; } +#if 0 + void handle_x_command(int argc, const char **argv); +#endif + static tty_font *load_tty_font(const char *); +}; + +tty_font *tty_font::load_tty_font(const char *s) +{ + tty_font *f = new tty_font(s); + if (!f->load()) { + delete f; + return 0; + } + const char *num = f->get_internal_name(); + long n; + if (num != 0 && (n = strtol(num, 0, 0)) != 0) + f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE)); + if (!underline_flag) + f->mode &= ~UNDERLINE_MODE; + if (!bold_flag) + f->mode &= ~BOLD_MODE; + if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE)) + f->mode = (f->mode & ~(BOLD_MODE|UNDERLINE_MODE)) | bold_underline_mode; + return f; +} + +tty_font::tty_font(const char *nm) +: font(nm), mode(0) +{ +} + +tty_font::~tty_font() +{ +} + +#if 0 +void tty_font::handle_x_command(int argc, const char **argv) +{ + if (argc >= 1 && strcmp(argv[0], "bold") == 0) + mode |= BOLD_MODE; + else if (argc >= 1 && strcmp(argv[0], "underline") == 0) + mode |= UNDERLINE_MODE; +} +#endif + +class glyph { + static glyph *free_list; +public: + glyph *next; + short hpos; + unsigned int code; + unsigned char mode; + void *operator new(size_t); + void operator delete(void *); + inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); } + inline int order() { return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE); } +}; + +glyph *glyph::free_list = 0; + +void *glyph::operator new(size_t) +{ + if (!free_list) { + const int BLOCK = 1024; + free_list = (glyph *)new char[sizeof(glyph) * BLOCK]; + for (int i = 0; i < BLOCK - 1; i++) + free_list[i].next = free_list + i + 1; + free_list[BLOCK - 1].next = 0; + } + glyph *p = free_list; + free_list = free_list->next; + p->next = 0; + return p; +} + +void glyph::operator delete(void *p) +{ + if (p) { + ((glyph *)p)->next = free_list; + free_list = (glyph *)p; + } +} + +class tty_printer : public printer { + int is_utf8; + glyph **lines; + int nlines; + int cached_v; + int cached_vpos; + void add_char(unsigned int, int, int, unsigned char); +public: + tty_printer(const char *device); + ~tty_printer(); + void set_char(int, font *, const environment *, int, const char *name); + void draw(int code, int *p, int np, const environment *env); + void special(char *arg, const environment *env, char type); + void put_char(unsigned int); + void begin_page(int) { } + void end_page(int page_length); + font *make_font(const char *); +}; + +tty_printer::tty_printer(const char *device) : cached_v(0) +{ + is_utf8 = !strcmp(device, "utf8"); + nlines = 66; + lines = new glyph *[nlines]; + for (int i = 0; i < nlines; i++) + lines[i] = 0; +} + +tty_printer::~tty_printer() +{ + a_delete lines; +} + +void tty_printer::set_char(int i, font *f, const environment *env, + int w, const char *name) +{ + if (w != font::hor) + fatal("width of character not equal to horizontal resolution"); + add_char(f->get_code(i), env->hpos, env->vpos, ((tty_font *)f)->get_mode()); +} + +void tty_printer::add_char(unsigned int c, int h, int v, unsigned char mode) +{ +#if 0 + // This is too expensive. + if (h % font::hor != 0) + fatal("horizontal position not a multiple of horizontal resolution"); +#endif + int hpos = h / font::hor; + if (hpos < SHRT_MIN || hpos > SHRT_MAX) { + error("character with ridiculous horizontal position discarded"); + return; + } + int vpos; + if (v == cached_v && cached_v != 0) + vpos = cached_vpos; + else { + if (v % font::vert != 0) + fatal("vertical position not a multiple of vertical resolution"); + vpos = v / font::vert; + if (vpos > nlines) { + glyph **old_lines = lines; + lines = new glyph *[vpos + 1]; + memcpy(lines, old_lines, nlines * sizeof(glyph *)); + for (int i = nlines; i <= vpos; i++) + lines[i] = 0; + a_delete old_lines; + nlines = vpos + 1; + } + // Note that the first output line corresponds to groff + // position font::vert. + if (vpos <= 0) { + error("character above first line discarded"); + return; + } + cached_v = v; + cached_vpos = vpos; + } + glyph *g = new glyph; + g->hpos = hpos; + g->code = c; + g->mode = mode; + + // The list will be reversed later. After reversal, it must be in + // increasing order of hpos, with CU specials before HDRAW characters + // before VDRAW characters before normal characters at each hpos, and + // otherwise in order of occurrence. + + glyph **pp; + for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next) + if ((*pp)->hpos < hpos + || ((*pp)->hpos == hpos && (*pp)->order() >= g->order())) + break; + g->next = *pp; + *pp = g; +} + +void tty_printer::special(char *arg, const environment *env, char type) +{ + if (type == 'u') + add_char(*arg - '0', env->hpos, env->vpos, CU_MODE); +} + +void tty_printer::draw(int code, int *p, int np, const environment *env) +{ + if (code != 'l' || !draw_flag) + return; + if (np != 2) { + error("2 arguments required for line"); + return; + } + if (p[0] == 0) { + // vertical line + int v = env->vpos; + int len = p[1]; + if (len < 0) { + v += len; + len = -len; + } + while (len >= 0) { + add_char('|', env->hpos, v, VDRAW_MODE); + len -= font::vert; + v += font::vert; + } + } + if (p[1] == 0) { + // horizontal line + int h = env->hpos; + int len = p[0]; + if (len < 0) { + h += len; + len = -len; + } + while (len >= 0) { + add_char('-', h, env->vpos, HDRAW_MODE); + len -= font::hor; + h += font::hor; + } + } +} + +void tty_printer::put_char(unsigned int wc) +{ + if (is_utf8 && wc >= 0x80) { + char buf[6 + 1]; + int count; + char *p = buf; + if (wc < 0x800) + count = 1, *p = (unsigned char)((wc >> 6) | 0xc0); + else if (wc < 0x10000) + count = 2, *p = (unsigned char)((wc >> 12) | 0xe0); + else if (wc < 0x200000) + count = 3, *p = (unsigned char)((wc >> 18) | 0xf0); + else if (wc < 0x4000000) + count = 4, *p = (unsigned char)((wc >> 24) | 0xf8); + else if (wc <= 0x7fffffff) + count = 5, *p = (unsigned char)((wc >> 30) | 0xfC); + else + return; + do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80); + while (count > 0); + *++p = '\0'; + fputs(buf, stdout); + } + else { + putchar(wc); + } +} + +int cu_flag = 0; + +void tty_printer::end_page(int page_length) +{ + if (page_length % font::vert != 0) + error("vertical position at end of page not multiple of vertical resolution"); + int lines_per_page = page_length / font::vert; + int last_line; + for (last_line = nlines; last_line > 0; last_line--) + if (lines[last_line - 1]) + break; +#if 0 + if (last_line > lines_per_page) { + error("characters past last line discarded"); + do { + --last_line; + while (lines[last_line]) { + glyph *tem = lines[last_line]; + lines[last_line] = tem->next; + delete tem; + } + } while (last_line > lines_per_page); + } +#endif + for (int i = 0; i < last_line; i++) { + glyph *p = lines[i]; + lines[i] = 0; + glyph *g = 0; + while (p) { + glyph *tem = p->next; + p->next = g; + g = p; + p = tem; + } + int hpos = 0; + glyph *nextp; + for (p = g; p; delete p, p = nextp) { + nextp = p->next; + if (p->mode & CU_MODE) { + cu_flag = p->code; + continue; + } + if (nextp && p->hpos == nextp->hpos) { + if (p->draw_mode() == HDRAW_MODE && + nextp->draw_mode() == VDRAW_MODE) { + nextp->code = '+'; + continue; + } + if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) { + nextp->code = p->code; + continue; + } + if (!overstrike_flag) + continue; + } + if (hpos > p->hpos) { + do { + putchar('\b'); + hpos--; + } while (hpos > p->hpos); + } + else { + if (horizontal_tab_flag) { + for (;;) { + int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH; + if (next_tab_pos > p->hpos) + break; + if (cu_flag) { + putchar('_'); + putchar('\b'); + } + putchar('\t'); + hpos = next_tab_pos; + } + } + for (; hpos < p->hpos; hpos++) { + if (cu_flag) { + putchar('_'); + putchar('\b'); + } + putchar(' '); + } + } + assert(hpos == p->hpos); + if (p->mode & UNDERLINE_MODE) { + putchar('_'); + putchar('\b'); + } + if (p->mode & BOLD_MODE) { + put_char(p->code); + putchar('\b'); + } + put_char(p->code); + hpos++; + } + putchar('\n'); + } + if (form_feed_flag) { + if (last_line < lines_per_page) + putchar('\f'); + } + else { + for (; last_line < lines_per_page; last_line++) + putchar('\n'); + } +} + +font *tty_printer::make_font(const char *nm) +{ + return tty_font::load_tty_font(nm); +} + +printer *make_printer() +{ + return new tty_printer(device); +} + +static void usage(FILE *stream); + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "F:vhfbuoBUd", long_options, NULL)) + != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + printf("GNU grotty (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'b': + // Do not embolden by overstriking. + bold_flag = 0; + break; + case 'u': + // Do not underline. + underline_flag = 0; + break; + case 'o': + // Do not overstrike (other than emboldening and underlining). + overstrike_flag = 0; + break; + case 'B': + // Do bold-underlining as bold. + bold_underline_mode = BOLD_MODE; + break; + case 'U': + // Do bold-underlining as underlining. + bold_underline_mode = UNDERLINE_MODE; + break; + case 'h': + // Use horizontal tabs. + horizontal_tab_flag = 1; + break; + case 'f': + form_feed_flag = 1; + break; + case 'F': + font::command_line_font_dir(optarg); + break; + case 'd': + // Ignore \D commands. + draw_flag = 0; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [-hfvbuodBU] [-F dir] [files ...]\n", + program_name); +} |