2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25 extern "C" const char *Version_string;
27 #define DEFAULT_LINEWIDTH 40
28 static int linewidth = DEFAULT_LINEWIDTH;
30 static int draw_flag = 1;
32 /* These values were chosen because:
34 (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
36 and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
38 The width in the groff font file is the product of MULTIPLIER and the
39 width in the tfm file. */
42 #define RES_7227 (RES/7227)
43 #define UNITWIDTH 131072
47 class dvi_font : public font {
48 dvi_font(const char *);
53 void handle_unknown_font_command(const char *command, const char *arg,
54 const char *filename, int lineno);
55 static dvi_font *load_dvi_font(const char *);
58 dvi_font *dvi_font::load_dvi_font(const char *s)
60 dvi_font *f = new dvi_font(s);
68 dvi_font::dvi_font(const char *nm)
69 : font(nm), checksum(0), design_size(0)
77 void dvi_font::handle_unknown_font_command(const char *command,
79 const char *filename, int lineno)
82 if (strcmp(command, "checksum") == 0) {
84 fatal_with_file_and_line(filename, lineno,
85 "`checksum' command requires an argument");
86 checksum = int(strtol(arg, &ptr, 10));
87 if (checksum == 0 && ptr == arg) {
88 fatal_with_file_and_line(filename, lineno, "bad checksum");
91 else if (strcmp(command, "designsize") == 0) {
93 fatal_with_file_and_line(filename, lineno,
94 "`designsize' command requires an argument");
95 design_size = int(strtol(arg, &ptr, 10));
96 if (design_size == 0 && ptr == arg) {
97 fatal_with_file_and_line(filename, lineno, "bad design size");
102 #define FONTS_MAX 256
107 output_font() : f(0) { }
110 class dvi_printer : public printer {
121 output_font output_font_table[FONTS_MAX];
131 void define_font(int);
133 void possibly_begin_line();
134 void set_color(color *);
162 void moveto(int, int);
163 void out_string(const char *);
164 void out_signed(unsigned char, int);
165 void out_unsigned(unsigned char, int);
166 void do_special(const char *);
170 font *make_font(const char *);
171 void begin_page(int);
173 void set_char(int, font *, const environment *, int w, const char *name);
174 void special(char *arg, const environment *env, char type);
176 void draw(int code, int *p, int np, const environment *env);
180 class draw_dvi_printer : public dvi_printer {
182 void set_line_thickness(const environment *);
183 void fill_next(const environment *);
187 void draw(int code, int *p, int np, const environment *env);
191 dvi_printer::dvi_printer()
192 : fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
193 cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
195 if (font::res != RES)
196 fatal("resolution must be %1", RES);
197 if (font::unitwidth != UNITWIDTH)
198 fatal("unitwidth must be %1", UNITWIDTH);
200 fatal("hor must be equal to 1");
202 fatal("vert must be equal to 1");
203 if (font::sizescale != SIZESCALE)
204 fatal("sizescale must be equal to %1", SIZESCALE);
205 max_drift = font::res/1000; // this is fairly arbitrary
209 dvi_printer::~dvi_printer()
215 draw_dvi_printer::draw_dvi_printer()
216 : output_pen_size(-1)
220 draw_dvi_printer::~draw_dvi_printer()
225 void dvi_printer::out1(int n)
231 void dvi_printer::out2(int n)
234 putc((n >> 8) & 0xff, fp);
238 void dvi_printer::out3(int n)
241 putc((n >> 16) & 0xff, fp);
242 putc((n >> 8) & 0xff, fp);
246 void dvi_printer::out4(int n)
249 putc((n >> 24) & 0xff, fp);
250 putc((n >> 16) & 0xff, fp);
251 putc((n >> 8) & 0xff, fp);
255 void dvi_printer::out_string(const char *s)
263 void dvi_printer::end_of_line()
273 void dvi_printer::possibly_begin_line()
276 have_pushed = pushed = 1;
283 int scale(int x, int z)
288 alpha = 16*z; beta = 16;
289 while (z >= 040000000L) {
296 sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
304 void dvi_printer::set_color(color *col)
308 unsigned int components[4];
309 color_scheme cs = col->get_components(components);
312 sprintf(buf, "color gray 0");
315 sprintf(buf, "color rgb %.3g %.3g %.3g",
316 double(Red) / color::MAX_COLOR_VAL,
317 double(Green) / color::MAX_COLOR_VAL,
318 double(Blue) / color::MAX_COLOR_VAL);
321 col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
324 sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
325 double(Cyan) / color::MAX_COLOR_VAL,
326 double(Magenta) / color::MAX_COLOR_VAL,
327 double(Yellow) / color::MAX_COLOR_VAL,
328 double(Black) / color::MAX_COLOR_VAL);
331 sprintf(buf, "color gray %.3g",
332 double(Gray) / color::MAX_COLOR_VAL);
338 void dvi_printer::set_char(int index, font *f, const environment *env,
339 int w, const char *name)
341 if (*env->col != cur_color)
343 int code = f->get_code(index);
344 if (env->size != cur_point_size || f != cur_font) {
346 cur_point_size = env->size;
349 if (i >= FONTS_MAX) {
350 fatal("too many output fonts required");
352 if (output_font_table[i].f == 0) {
353 output_font_table[i].f = (dvi_font *)cur_font;
354 output_font_table[i].point_size = cur_point_size;
357 if (output_font_table[i].f == cur_font
358 && output_font_table[i].point_size == cur_point_size)
363 int distance = env->hpos - cur_h;
364 if (env->hpos != end_h && distance != 0) {
365 out_signed(right1, distance);
368 else if (distance > max_drift) {
369 out_signed(right1, distance - max_drift);
370 cur_h = env->hpos - max_drift;
372 else if (distance < -max_drift) {
373 out_signed(right1, distance + max_drift);
374 cur_h = env->hpos + max_drift;
376 if (env->vpos != cur_v) {
377 out_signed(down1, env->vpos - cur_v);
380 possibly_begin_line();
381 end_h = env->hpos + w;
382 cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER,
383 cur_point_size*RES_7227);
388 if (code >= 0 && code <= 127)
391 out_unsigned(set1, code);
394 void dvi_printer::define_font(int i)
396 out_unsigned(fnt_def1, i);
397 dvi_font *f = output_font_table[i].f;
399 out4(output_font_table[i].point_size*RES_7227);
400 out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
401 const char *nm = f->get_internal_name();
406 void dvi_printer::set_font(int i)
408 if (i >= 0 && i <= 63)
411 out_unsigned(fnt1, i);
414 void dvi_printer::out_signed(unsigned char base, int param)
416 if (-128 <= param && param < 128) {
420 else if (-32768 <= param && param < 32768) {
424 else if (-(1 << 23) <= param && param < (1 << 23)) {
434 void dvi_printer::out_unsigned(unsigned char base, int param)
441 else if (param < 65536) {
445 else if (param < (1 << 24)) {
460 void dvi_printer::preamble()
470 void dvi_printer::postamble()
472 int tem = byte_count;
480 out2(have_pushed); // stack depth
483 for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
488 for (i = 0; i < 4 || byte_count % 4 != 0; i++)
492 void dvi_printer::begin_page(int i)
495 int tem = byte_count;
498 for (int j = 1; j < 10; j++)
502 if (cur_color != default_color)
503 set_color(&cur_color);
504 // By convention position (0,0) in a dvi file is placed at (1in, 1in).
510 void dvi_printer::end_page(int)
512 set_color(&default_color);
519 void draw_dvi_printer::end_page(int len)
521 dvi_printer::end_page(len);
522 output_pen_size = -1;
525 void dvi_printer::do_special(const char *s)
530 possibly_begin_line();
531 out_unsigned(xxx1, len);
536 void dvi_printer::special(char *arg, const environment *env, char type)
540 moveto(env->hpos, env->vpos);
544 void dvi_printer::moveto(int h, int v)
547 out_signed(right1, h - cur_h);
553 out_signed(down1, v - cur_v);
561 void dvi_printer::draw(int code, int *p, int np, const environment *env)
565 int height = 0, width = 0;
567 if (line_thickness < 0)
568 thickness = env->size*RES_7227*linewidth/1000;
569 else if (line_thickness > 0)
570 thickness = line_thickness;
574 error("2 arguments required for line");
576 else if (p[0] == 0) {
579 x = env->hpos - thickness/2;
580 y = env->vpos + p[1] + thickness/2;
581 height = p[1] + thickness;
585 x = env->hpos - thickness/2;
586 y = env->vpos + thickness/2;
587 height = thickness - p[1];
591 else if (p[1] == 0) {
593 x = env->hpos - thickness/2;
594 y = env->vpos + thickness/2;
596 width = p[0] + thickness;
599 x = env->hpos - p[0] - thickness/2;
600 y = env->vpos + thickness/2;
602 width = thickness - p[0];
612 else if (code == 't') {
617 // troff gratuitously adds an extra 0
618 if (np != 1 && np != 2)
619 error("0 or 1 argument required for thickness");
621 line_thickness = p[0];
624 else if (code == 'R') {
626 error("2 arguments required for rule");
627 else if (p[0] != 0 || p[1] != 0) {
648 // XXX Will this overflow?
650 inline int milliinches(int n)
652 return (n*1000 + font::res/2)/font::res;
655 void draw_dvi_printer::set_line_thickness(const environment *env)
658 = milliinches(line_thickness < 0
659 // Will this overflow?
660 ? env->size*RES_7227*linewidth/1000
662 if (desired_pen_size != output_pen_size) {
664 sprintf(buf, "pn %d", desired_pen_size);
666 output_pen_size = desired_pen_size;
670 void draw_dvi_printer::fill_next(const environment *env)
673 if (env->fill->is_default())
676 // currently, only BW support
677 env->fill->get_gray(&g);
680 sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
684 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
693 // troff adds an extra argument to C
694 if (np != 1 && !(code == 'C' && np == 2)) {
695 error("1 argument required for circle");
698 moveto(env->hpos+p[0]/2, env->vpos);
702 set_line_thickness(env);
704 rad = milliinches(p[0]/2);
705 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
706 (fill_flag ? "ia" : "ar"),
713 error("2 arguments required for line");
716 moveto(env->hpos, env->vpos);
717 set_line_thickness(env);
718 do_special("pa 0 0");
719 sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
728 error("2 arguments required for ellipse");
731 moveto(env->hpos+p[0]/2, env->vpos);
734 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
735 (fill_flag ? "ia" : "ar"),
737 milliinches(p[1]/2));
746 error("even number of arguments required for polygon");
750 error("no arguments for polygon");
753 moveto(env->hpos, env->vpos);
757 set_line_thickness(env);
758 do_special("pa 0 0");
760 for (int i = 0; i < np; i += 2) {
763 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
766 do_special("pa 0 0");
767 do_special(fill_flag ? "ip" : "fp");
773 error("even number of arguments required for spline");
777 error("no arguments for spline");
780 moveto(env->hpos, env->vpos);
781 set_line_thickness(env);
782 do_special("pa 0 0");
784 for (int i = 0; i < np; i += 2) {
787 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
796 error("4 arguments required for arc");
799 set_line_thickness(env);
801 if (adjust_arc_center(p, c)) {
802 int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
803 moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
804 sprintf(buf, "ar 0 0 %d %d %f %f",
807 atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]),
808 atan2(-c[1], -c[0]));
812 moveto(env->hpos, env->vpos);
813 do_special("pa 0 0");
816 milliinches(p[0] + p[2]),
817 milliinches(p[1] + p[3]));
829 // troff gratuitously adds an extra 0
830 if (np != 1 && np != 2) {
831 error("0 or 1 argument required for thickness");
834 line_thickness = p[0];
841 error("2 arguments required for rule");
867 error("unrecognised drawing command `%1'", char(code));
872 font *dvi_printer::make_font(const char *nm)
874 return dvi_font::load_dvi_font(nm);
877 printer *make_printer()
880 return new draw_dvi_printer;
882 return new dvi_printer;
885 static void usage(FILE *stream);
887 int main(int argc, char **argv)
889 program_name = argv[0];
890 static char stderr_buf[BUFSIZ];
891 setbuf(stderr, stderr_buf);
893 static const struct option long_options[] = {
894 { "help", no_argument, 0, CHAR_MAX + 1 },
895 { "version", no_argument, 0, 'v' },
898 while ((c = getopt_long(argc, argv, "F:vw:d", long_options, NULL)) != EOF)
902 printf("GNU grodvi (groff) version %s\n", Version_string);
907 if (sscanf(optarg, "%d", &linewidth) != 1
908 || linewidth < 0 || linewidth > 1000) {
909 error("bad line width");
910 linewidth = DEFAULT_LINEWIDTH;
917 font::command_line_font_dir(optarg);
919 case CHAR_MAX + 1: // --help
931 SET_BINARY(fileno(stdout));
936 for (int i = optind; i < argc; i++)
943 static void usage(FILE *stream)
945 fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",