groff before CVS: release 1.06
[s-roff.git] / grodvi / dvi.cc
blobab17f0f01d9c084eac24d58ffe4bc28331adcc51
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "driver.h"
23 #define DEFAULT_LINEWIDTH 40
24 static int linewidth = DEFAULT_LINEWIDTH;
26 static int draw_flag = 1;
28 /* These values were chosen because:
30 (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
32 and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
34 The width in the groff font file is the product of MULTIPLIER and the
35 width in the tfm file. */
37 #define RES 57816
38 #define RES_7227 (RES/7227)
39 #define UNITWIDTH 131072
40 #define SIZESCALE 100
41 #define MULTIPLIER 1
43 #define FILL_MAX 1000
45 class dvi_font : public font {
46 dvi_font(const char *);
47 public:
48 int checksum;
49 int design_size;
50 ~dvi_font();
51 void handle_unknown_font_command(const char *command, const char *arg,
52 const char *filename, int lineno);
53 static dvi_font *load_dvi_font(const char *);
56 dvi_font *dvi_font::load_dvi_font(const char *s)
58 dvi_font *f = new dvi_font(s);
59 if (!f->load()) {
60 delete f;
61 return 0;
63 return f;
66 dvi_font::dvi_font(const char *nm)
67 : font(nm), checksum(0), design_size(0)
71 dvi_font::~dvi_font()
75 void dvi_font::handle_unknown_font_command(const char *command,
76 const char *arg,
77 const char *filename, int lineno)
79 char *ptr;
80 if (strcmp(command, "checksum") == 0) {
81 if (arg == 0)
82 fatal_with_file_and_line(filename, lineno,
83 "`checksum' command requires an argument");
84 checksum = int(strtol(arg, &ptr, 10));
85 if (checksum == 0 && ptr == arg) {
86 fatal_with_file_and_line(filename, lineno, "bad checksum");
89 else if (strcmp(command, "designsize") == 0) {
90 if (arg == 0)
91 fatal_with_file_and_line(filename, lineno,
92 "`designsize' command requires an argument");
93 design_size = int(strtol(arg, &ptr, 10));
94 if (design_size == 0 && ptr == arg) {
95 fatal_with_file_and_line(filename, lineno, "bad design size");
100 #define FONTS_MAX 256
102 struct output_font {
103 dvi_font *f;
104 int point_size;
105 output_font() : f(0) { }
108 class dvi_printer : public printer {
109 FILE *fp;
110 int max_drift;
111 int byte_count;
112 int last_bop;
113 int page_count;
114 int cur_h;
115 int cur_v;
116 int end_h;
117 int max_h;
118 int max_v;
119 output_font output_font_table[FONTS_MAX];
120 font *cur_font;
121 int cur_point_size;
122 int pushed;
123 int pushed_h;
124 int pushed_v;
125 int have_pushed;
126 void preamble();
127 void postamble();
128 void define_font(int);
129 void set_font(int);
130 void possibly_begin_line();
131 protected:
132 enum {
133 id_byte = 2,
134 set1 = 128,
135 put1 = 133,
136 put_rule = 137,
137 bop = 139,
138 eop = 140,
139 push = 141,
140 pop = 142,
141 right1 = 143,
142 down1 = 157,
143 fnt_num_0 = 171,
144 fnt1 = 235,
145 xxx1 = 239,
146 fnt_def1 = 243,
147 pre = 247,
148 post = 248,
149 post_post = 249,
150 filler = 223
152 int line_thickness;
154 void out1(int);
155 void out2(int);
156 void out3(int);
157 void out4(int);
158 void moveto(int, int);
159 void out_string(const char *);
160 void out_signed(unsigned char, int);
161 void out_unsigned(unsigned char, int);
162 void do_special(const char *);
163 public:
164 dvi_printer();
165 ~dvi_printer();
166 font *make_font(const char *);
167 void begin_page(int);
168 void end_page();
169 void set_char(int, font *, const environment *, int w);
170 void special(char *arg, const environment *env);
171 void end_of_line();
172 void draw(int code, int *p, int np, const environment *env);
176 class draw_dvi_printer : public dvi_printer {
177 int output_pen_size;
178 int fill;
179 void set_line_thickness(const environment *);
180 void fill_next();
181 public:
182 draw_dvi_printer();
183 ~draw_dvi_printer();
184 void draw(int code, int *p, int np, const environment *env);
185 void end_page();
188 dvi_printer::dvi_printer()
189 : byte_count(0), last_bop(-1), page_count(0), cur_font(0), fp(stdout),
190 max_h(0), max_v(0), pushed(0), line_thickness(-1), cur_point_size(-1)
192 if (font::res != RES)
193 fatal("resolution must be %1", RES);
194 if (font::unitwidth != UNITWIDTH)
195 fatal("unitwidth must be %1", UNITWIDTH);
196 if (font::hor != 1)
197 fatal("hor must be equal to 1");
198 if (font::vert != 1)
199 fatal("vert must be equal to 1");
200 if (font::sizescale != SIZESCALE)
201 fatal("sizescale must be equal to %1", SIZESCALE);
202 max_drift = font::res/1000; // this is fairly arbitrary
203 preamble();
206 dvi_printer::~dvi_printer()
208 postamble();
212 draw_dvi_printer::draw_dvi_printer()
213 : output_pen_size(-1), fill(FILL_MAX)
217 draw_dvi_printer::~draw_dvi_printer()
222 void dvi_printer::out1(int n)
224 byte_count += 1;
225 putc(n & 0xff, fp);
228 void dvi_printer::out2(int n)
230 byte_count += 2;
231 putc((n >> 8) & 0xff, fp);
232 putc(n & 0xff, fp);
235 void dvi_printer::out3(int n)
237 byte_count += 3;
238 putc((n >> 16) & 0xff, fp);
239 putc((n >> 8) & 0xff, fp);
240 putc(n & 0xff, fp);
243 void dvi_printer::out4(int n)
245 byte_count += 4;
246 putc((n >> 24) & 0xff, fp);
247 putc((n >> 16) & 0xff, fp);
248 putc((n >> 8) & 0xff, fp);
249 putc(n & 0xff, fp);
252 void dvi_printer::out_string(const char *s)
254 out1(strlen(s));
255 while (*s != 0)
256 out1(*s++);
260 void dvi_printer::end_of_line()
262 if (pushed) {
263 out1(pop);
264 pushed = 0;
265 cur_h = pushed_h;
266 cur_v = pushed_v;
270 void dvi_printer::possibly_begin_line()
272 if (!pushed) {
273 have_pushed = pushed = 1;
274 pushed_h = cur_h;
275 pushed_v = cur_v;
276 out1(push);
280 int scale(int x, int z)
282 int sw;
283 int a, b, c, d;
284 int alpha, beta;
285 alpha = 16*z; beta = 16;
286 while (z >= 040000000L) {
287 z /= 2; beta /= 2;
289 d = x & 255;
290 c = (x >> 8) & 255;
291 b = (x >> 16) & 255;
292 a = (x >> 24) & 255;
293 sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
294 if (a == 255)
295 sw -= alpha;
296 else
297 assert(a == 0);
298 return sw;
302 void dvi_printer::set_char(int index, font *f, const environment *env, int w)
304 int code = f->get_code(index);
305 if (env->size != cur_point_size || f != cur_font) {
306 cur_font = f;
307 cur_point_size = env->size;
308 for (int i = 0;; i++) {
309 if (i >= FONTS_MAX) {
310 fatal("too many output fonts required");
312 if (output_font_table[i].f == 0) {
313 output_font_table[i].f = (dvi_font *)cur_font;
314 output_font_table[i].point_size = cur_point_size;
315 define_font(i);
317 if (output_font_table[i].f == cur_font
318 && output_font_table[i].point_size == cur_point_size)
319 break;
321 set_font(i);
323 int distance = env->hpos - cur_h;
324 if (env->hpos != end_h && distance != 0) {
325 out_signed(right1, distance);
326 cur_h = env->hpos;
328 else if (distance > max_drift) {
329 out_signed(right1, distance - max_drift);
330 cur_h = env->hpos - max_drift;
332 else if (distance < -max_drift) {
333 out_signed(right1, distance + max_drift);
334 cur_h = env->hpos + max_drift;
336 if (env->vpos != cur_v) {
337 out_signed(down1, env->vpos - cur_v);
338 cur_v = env->vpos;
340 possibly_begin_line();
341 end_h = env->hpos + w;
342 cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER,
343 cur_point_size*RES_7227);
344 if (cur_h > max_h)
345 max_h = cur_h;
346 if (cur_v > max_v)
347 max_v = cur_v;
348 if (code >= 0 && code <= 127)
349 out1(code);
350 else
351 out_unsigned(set1, code);
354 void dvi_printer::define_font(int i)
356 out_unsigned(fnt_def1, i);
357 dvi_font *f = output_font_table[i].f;
358 out4(f->checksum);
359 out4(output_font_table[i].point_size*RES_7227);
360 out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
361 const char *nm = f->get_internal_name();
362 out1(0);
363 out_string(nm);
366 void dvi_printer::set_font(int i)
368 if (i >= 0 && i <= 63)
369 out1(fnt_num_0 + i);
370 else
371 out_unsigned(fnt1, i);
374 void dvi_printer::out_signed(unsigned char base, int param)
376 if (-128 <= param && param < 128) {
377 out1(base);
378 out1(param);
380 else if (-32768 <= param && param < 32768) {
381 out1(base+1);
382 out2(param);
384 else if (-(1 << 23) <= param && param < (1 << 23)) {
385 out1(base+2);
386 out3(param);
388 else {
389 out1(base+3);
390 out4(param);
394 void dvi_printer::out_unsigned(unsigned char base, int param)
396 if (param >= 0) {
397 if (param < 256) {
398 out1(base);
399 out1(param);
401 else if (param < 65536) {
402 out1(base+1);
403 out2(param);
405 else if (param < (1 << 24)) {
406 out1(base+2);
407 out3(param);
409 else {
410 out1(base+3);
411 out4(param);
414 else {
415 out1(base+3);
416 out4(param);
420 void dvi_printer::preamble()
422 out1(pre);
423 out1(id_byte);
424 out4(254000);
425 out4(font::res);
426 out4(1000);
427 out1(0);
430 void dvi_printer::postamble()
432 int tem = byte_count;
433 out1(post);
434 out4(last_bop);
435 out4(254000);
436 out4(font::res);
437 out4(1000);
438 out4(max_v);
439 out4(max_h);
440 out2(have_pushed); // stack depth
441 out2(page_count);
442 int i;
443 for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
444 define_font(i);
445 out1(post_post);
446 out4(tem);
447 out1(id_byte);
448 for (i = 0; i < 4 || byte_count % 4 != 0; i++)
449 out1(filler);
452 void dvi_printer::begin_page(int i)
454 page_count++;
455 int tem = byte_count;
456 out1(bop);
457 out4(i);
458 for (int j = 1; j < 10; j++)
459 out4(0);
460 out4(last_bop);
461 last_bop = tem;
462 // By convention position (0,0) in a dvi file is placed at (1in, 1in).
463 cur_h = font::res;
464 cur_v = font::res;
465 end_h = 0;
468 void dvi_printer::end_page()
470 if (pushed)
471 end_of_line();
472 out1(eop);
473 cur_font = 0;
476 void draw_dvi_printer::end_page()
478 dvi_printer::end_page();
479 output_pen_size = -1;
482 void dvi_printer::do_special(const char *s)
484 int len = strlen(s);
485 if (len == 0)
486 return;
487 possibly_begin_line();
488 out_unsigned(xxx1, len);
489 while (*s)
490 out1(*s++);
493 void dvi_printer::special(char *arg, const environment *env)
495 moveto(env->hpos, env->vpos);
496 do_special(arg);
499 void dvi_printer::moveto(int h, int v)
501 if (h != cur_h) {
502 out_signed(right1, h - cur_h);
503 cur_h = h;
504 if (cur_h > max_h)
505 max_h = cur_h;
507 if (v != cur_v) {
508 out_signed(down1, v - cur_v);
509 cur_v = v;
510 if (cur_v > max_v)
511 max_v = cur_v;
513 end_h = 0;
516 void dvi_printer::draw(int code, int *p, int np, const environment *env)
518 if (code == 'l') {
519 int x, y;
520 int height = 0, width;
521 int thickness;
522 if (line_thickness < 0)
523 thickness = env->size*RES_7227*linewidth/1000;
524 else if (line_thickness > 0)
525 thickness = line_thickness;
526 else
527 thickness = 1;
528 if (np != 2) {
529 error("2 arguments required for line");
531 else if (p[0] == 0) {
532 // vertical rule
533 if (p[1] > 0) {
534 x = env->hpos - thickness/2;
535 y = env->vpos + p[1] + thickness/2;
536 height = p[1] + thickness;
537 width = thickness;
539 else if (p[1] < 0) {
540 x = env->hpos - thickness/2;
541 y = env->vpos + thickness/2;
542 height = thickness - p[1];
543 width = thickness;
546 else if (p[1] == 0) {
547 if (p[0] > 0) {
548 x = env->hpos - thickness/2;
549 y = env->vpos + thickness/2;
550 height = thickness;
551 width = p[0] + thickness;
553 else if (p[0] < 0) {
554 x = env->hpos - p[0] - thickness/2;
555 y = env->vpos + thickness/2;
556 height = thickness;
557 width = thickness - p[0];
560 if (height != 0) {
561 moveto(x, y);
562 out1(put_rule);
563 out4(height);
564 out4(width);
567 else if (code == 't') {
568 if (np == 0) {
569 line_thickness = -1;
571 else {
572 // troff gratuitously adds an extra 0
573 if (np != 1 && np != 2)
574 error("0 or 1 argument required for thickness");
575 else
576 line_thickness = p[0];
579 else if (code == 'R') {
580 if (np != 2)
581 error("2 arguments required for rule");
582 else if (p[0] != 0 || p[1] != 0) {
583 int dh = p[0];
584 int dv = p[1];
585 int oh = env->hpos;
586 int ov = env->vpos;
587 if (dv > 0) {
588 ov += dv;
589 dv = -dv;
591 if (dh < 0) {
592 oh += dh;
593 dh = -dh;
595 moveto(oh, ov);
596 out1(put_rule);
597 out4(-dv);
598 out4(dh);
603 // XXX Will this overflow?
605 inline int milliinches(int n)
607 return (n*1000 + font::res/2)/font::res;
610 void draw_dvi_printer::set_line_thickness(const environment *env)
612 int desired_pen_size
613 = milliinches(line_thickness < 0
614 // Will this overflow?
615 ? env->size*RES_7227*linewidth/1000
616 : line_thickness);
617 if (desired_pen_size != output_pen_size) {
618 char buf[256];
619 sprintf(buf, "pn %d", desired_pen_size);
620 do_special(buf);
621 output_pen_size = desired_pen_size;
625 void draw_dvi_printer::fill_next()
627 char buf[256];
628 sprintf(buf, "sh %.3f", double(fill)/FILL_MAX);
629 do_special(buf);
632 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
634 char buf[1024];
635 int fill_flag = 0;
636 switch (code) {
637 case 'C':
638 fill_flag = 1;
639 // fall through
640 case 'c':
641 // troff adds an extra argument to C
642 if (np != 1 && !(code == 'C' && np == 2)) {
643 error("1 argument required for circle");
644 break;
646 moveto(env->hpos+p[0]/2, env->vpos);
647 if (fill_flag)
648 fill_next();
649 else
650 set_line_thickness(env);
651 int rad;
652 rad = milliinches(p[0]/2);
653 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
654 (fill_flag ? "ia" : "ar"),
655 rad,
656 rad);
657 do_special(buf);
658 break;
659 case 'l':
660 if (np != 2) {
661 error("2 arguments required for line");
662 break;
664 moveto(env->hpos, env->vpos);
665 set_line_thickness(env);
666 do_special("pa 0 0");
667 sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
668 do_special(buf);
669 do_special("fp");
670 break;
671 case 'E':
672 fill_flag = 1;
673 // fall through
674 case 'e':
675 if (np != 2) {
676 error("2 arguments required for ellipse");
677 break;
679 moveto(env->hpos+p[0]/2, env->vpos);
680 if (fill_flag)
681 fill_next();
682 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
683 (fill_flag ? "ia" : "ar"),
684 milliinches(p[0]/2),
685 milliinches(p[1]/2));
686 do_special(buf);
687 break;
688 case 'P':
689 fill_flag = 1;
690 // fall through
691 case 'p':
693 if (np & 1) {
694 error("even number of arguments required for polygon");
695 break;
697 if (np == 0) {
698 error("no arguments for polygon");
699 break;
701 moveto(env->hpos, env->vpos);
702 if (fill_flag)
703 fill_next();
704 else
705 set_line_thickness(env);
706 do_special("pa 0 0");
707 int h = 0, v = 0;
708 for (int i = 0; i < np; i += 2) {
709 h += p[i];
710 v += p[i+1];
711 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
712 do_special(buf);
714 do_special("pa 0 0");
715 do_special(fill_flag ? "ip" : "fp");
716 break;
718 case '~':
720 if (np & 1) {
721 error("even number of arguments required for spline");
722 break;
724 if (np == 0) {
725 error("no arguments for spline");
726 break;
728 moveto(env->hpos, env->vpos);
729 set_line_thickness(env);
730 do_special("pa 0 0");
731 int h = 0, v = 0;
732 for (int i = 0; i < np; i += 2) {
733 h += p[i];
734 v += p[i+1];
735 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
736 do_special(buf);
738 do_special("sp");
739 break;
741 case 'a':
743 if (np != 4) {
744 error("4 arguments required for arc");
745 break;
747 set_line_thickness(env);
748 int ch = p[0];
749 int cv = p[1];
750 int eh = p[0] + p[2];
751 int ev = p[1] + p[3];
752 double n = (double(ch)*eh) + (double(cv)*ev);
753 if (n == 0) {
754 moveto(env->hpos, env->vpos);
755 do_special("pa 0 0");
756 sprintf(buf, "pa %d %d", milliinches(eh), milliinches(ev));
757 do_special(buf);
758 do_special("fp");
760 else {
761 double k = (double(eh)*eh + double(ev)*ev)/(2.0*n);
762 double h = k*ch;
763 double v = k*cv;
764 double start_angle = atan2(-v, -h);
765 double end_angle = atan2(ev - v, eh - h);
766 int rad = milliinches(int(sqrt(h*h + v*v) + .5));
767 moveto(env->hpos + int(h), env->vpos + int(v));
768 sprintf(buf, "ar 0 0 %d %d %f %f",
769 rad,
770 rad,
771 end_angle,
772 start_angle);
773 do_special(buf);
775 break;
777 case 't':
779 if (np == 0) {
780 line_thickness = -1;
782 else {
783 // troff gratuitously adds an extra 0
784 if (np != 1 && np != 2) {
785 error("0 or 1 argument required for thickness");
786 break;
788 line_thickness = p[0];
790 break;
792 case 'f':
794 if (np != 1 && np != 2) {
795 error("1 argument required for fill");
796 break;
798 fill = p[0];
799 if (fill < 0 || fill > FILL_MAX)
800 fill = FILL_MAX;
801 break;
803 case 'R':
805 if (np != 2) {
806 error("2 arguments required for rule");
807 break;
809 int dh = p[0];
810 if (dh == 0)
811 break;
812 int dv = p[1];
813 if (dv == 0)
814 break;
815 int oh = env->hpos;
816 int ov = env->vpos;
817 if (dv > 0) {
818 ov += dv;
819 dv = -dv;
821 if (dh < 0) {
822 oh += dh;
823 dh = -dh;
825 moveto(oh, ov);
826 out1(put_rule);
827 out4(-dv);
828 out4(dh);
829 break;
831 default:
832 error("unrecognised drawing command `%1'", char(code));
833 break;
837 font *dvi_printer::make_font(const char *nm)
839 return dvi_font::load_dvi_font(nm);
842 printer *make_printer()
844 if (draw_flag)
845 return new draw_dvi_printer;
846 else
847 return new dvi_printer;
850 static void usage();
852 int main(int argc, char **argv)
854 program_name = argv[0];
855 static char stderr_buf[BUFSIZ];
856 setbuf(stderr, stderr_buf);
857 int c;
858 while ((c = getopt(argc, argv, "F:vw:d")) != EOF)
859 switch(c) {
860 case 'v':
862 extern const char *version_string;
863 fprintf(stderr, "grodvi version %s\n", version_string);
864 fflush(stderr);
865 break;
867 case 'w':
868 if (sscanf(optarg, "%d", &linewidth) != 1
869 || linewidth < 0 || linewidth > 1000) {
870 error("bad line width");
871 linewidth = DEFAULT_LINEWIDTH;
873 break;
874 case 'd':
875 draw_flag = 0;
876 break;
877 case 'F':
878 font::command_line_font_dir(optarg);
879 break;
880 case '?':
881 usage();
882 break;
883 default:
884 assert(0);
886 if (optind >= argc)
887 do_file("-");
888 else {
889 for (int i = optind; i < argc; i++)
890 do_file(argv[i]);
892 delete pr;
893 exit(0);
896 static void usage()
898 fprintf(stderr, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
899 program_name);
900 exit(1);