home *** CD-ROM | disk | FTP | other *** search
- // -*- C++ -*-
- /* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
- Written by James Clark (jjc@jclark.uucp)
-
- 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 1, 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 LICENSE. If not, write to the Free Software
- Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "driver.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(int argc, const char **argv);
- 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(int argc, const char **argv)
- {
- char *ptr;
- if (strcmp(argv[0], "checksum") == 0) {
- if (argc != 2)
- fatal("`checksum' command requires exactly 1 argument");
- checksum = int(strtol(argv[1], &ptr, 10));
- if (checksum == 0 && ptr == argv[1]) {
- fatal("bad checksum");
- }
- }
- else if (strcmp(argv[0], "designsize") == 0) {
- if (argc != 2)
- fatal("`designsize' command requires exactly 1 argument");
- design_size = int(strtol(argv[1], &ptr, 10));
- if (design_size == 0 && ptr == argv[1]) {
- fatal("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();
- void set_char(int, font *, const environment *, int w);
- void special(char *arg, const environment *env);
- 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();
- };
-
- dvi_printer::dvi_printer()
- : byte_count(0), last_bop(-1), page_count(0), cur_font(0), fp(stdout),
- max_h(0), max_v(0), 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)
- {
- unsigned char code = f->get_code(index);
- if (env->size != cur_point_size || f != cur_font) {
- cur_font = f;
- cur_point_size = env->size;
- for (int 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 <= 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()
- {
- if (pushed)
- end_of_line();
- out1(eop);
- cur_font = 0;
- }
-
- void draw_dvi_printer::end_page()
- {
- dvi_printer::end_page();
- 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)
- {
- 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 = 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);
- int ch = p[0];
- int cv = p[1];
- int eh = p[0] + p[2];
- int ev = p[1] + p[3];
- double n = (double(ch)*eh) + (double(cv)*ev);
- if (n == 0) {
- moveto(env->hpos, env->vpos);
- do_special("pa 0 0");
- sprintf(buf, "pa %d %d", milliinches(eh), milliinches(ev));
- do_special(buf);
- do_special("fp");
- }
- else {
- double k = (double(eh)*eh + double(ev)*ev)/(2.0*n);
- double h = k*ch;
- double v = k*cv;
- double start_angle = atan2(-v, -h);
- double end_angle = atan2(ev - v, eh - h);
- int rad = milliinches(int(sqrt(h*h + v*v) + .5));
- moveto(env->hpos + int(h), env->vpos + int(v));
- sprintf(buf, "ar 0 0 %d %d %f %f",
- rad,
- rad,
- end_angle,
- start_angle);
- do_special(buf);
- }
- 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();
-
- int main(int argc, char **argv)
- {
- program_name = argv[0];
- static char stderr_buf[BUFSIZ];
- setbuf(stderr, stderr_buf);
- int c;
- while ((c = getopt(argc, argv, "F:vw:d")) != EOF)
- switch(c) {
- case 'v':
- {
- extern const char *version_string;
- fprintf(stderr, "grodvi version %s\n", version_string);
- fflush(stderr);
- 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 '?':
- usage();
- break;
- default:
- assert(0);
- }
- if (optind >= argc)
- do_file("-");
- else {
- for (int i = optind; i < argc; i++)
- do_file(argv[i]);
- }
- delete pr;
- exit(0);
- }
-
- static void usage()
- {
- fprintf(stderr, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
- program_name);
- exit(1);
- }
-