Don't use .Xo/.Xc. Fix date format.
[netbsd-mini2440.git] / gnu / usr.bin / groff / dvi / dvi.c
blobf56ff2e1e27b75810fa6a73eca906fafbae6e399
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
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 1, 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 LICENSE. 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(int argc, const char **argv);
52 static dvi_font *load_dvi_font(const char *);
55 dvi_font *dvi_font::load_dvi_font(const char *s)
57 dvi_font *f = new dvi_font(s);
58 if (!f->load()) {
59 delete f;
60 return 0;
62 return f;
65 dvi_font::dvi_font(const char *nm)
66 : font(nm), checksum(0), design_size(0)
70 dvi_font::~dvi_font()
74 void dvi_font::handle_unknown_font_command(int argc, const char **argv)
76 char *ptr;
77 if (strcmp(argv[0], "checksum") == 0) {
78 if (argc != 2)
79 fatal("`checksum' command requires exactly 1 argument");
80 checksum = int(strtol(argv[1], &ptr, 10));
81 if (checksum == 0 && ptr == argv[1]) {
82 fatal("bad checksum");
85 else if (strcmp(argv[0], "designsize") == 0) {
86 if (argc != 2)
87 fatal("`designsize' command requires exactly 1 argument");
88 design_size = int(strtol(argv[1], &ptr, 10));
89 if (design_size == 0 && ptr == argv[1]) {
90 fatal("bad design size");
95 #define FONTS_MAX 256
97 struct output_font {
98 dvi_font *f;
99 int point_size;
100 output_font() : f(0) { }
103 class dvi_printer : public printer {
104 FILE *fp;
105 int max_drift;
106 int byte_count;
107 int last_bop;
108 int page_count;
109 int cur_h;
110 int cur_v;
111 int end_h;
112 int max_h;
113 int max_v;
114 output_font output_font_table[FONTS_MAX];
115 font *cur_font;
116 int cur_point_size;
117 int pushed;
118 int pushed_h;
119 int pushed_v;
120 int have_pushed;
121 void preamble();
122 void postamble();
123 void define_font(int);
124 void set_font(int);
125 void possibly_begin_line();
126 protected:
127 enum {
128 id_byte = 2,
129 set1 = 128,
130 put1 = 133,
131 put_rule = 137,
132 bop = 139,
133 eop = 140,
134 push = 141,
135 pop = 142,
136 right1 = 143,
137 down1 = 157,
138 fnt_num_0 = 171,
139 fnt1 = 235,
140 xxx1 = 239,
141 fnt_def1 = 243,
142 pre = 247,
143 post = 248,
144 post_post = 249,
145 filler = 223,
147 int line_thickness;
149 void out1(int);
150 void out2(int);
151 void out3(int);
152 void out4(int);
153 void moveto(int, int);
154 void out_string(const char *);
155 void out_signed(unsigned char, int);
156 void out_unsigned(unsigned char, int);
157 void do_special(const char *);
158 public:
159 dvi_printer();
160 ~dvi_printer();
161 font *make_font(const char *);
162 void begin_page(int);
163 void end_page();
164 void set_char(int, font *, const environment *, int w);
165 void special(char *arg, const environment *env);
166 void end_of_line();
167 void draw(int code, int *p, int np, const environment *env);
171 class draw_dvi_printer : public dvi_printer {
172 int output_pen_size;
173 int fill;
174 void set_line_thickness(const environment *);
175 void fill_next();
176 public:
177 draw_dvi_printer();
178 ~draw_dvi_printer();
179 void draw(int code, int *p, int np, const environment *env);
180 void end_page();
183 dvi_printer::dvi_printer()
184 : byte_count(0), last_bop(-1), page_count(0), cur_font(0), fp(stdout),
185 max_h(0), max_v(0), pushed(0), line_thickness(-1)
187 if (font::res != RES)
188 fatal("resolution must be %1", RES);
189 if (font::unitwidth != UNITWIDTH)
190 fatal("unitwidth must be %1", UNITWIDTH);
191 if (font::hor != 1)
192 fatal("hor must be equal to 1");
193 if (font::vert != 1)
194 fatal("vert must be equal to 1");
195 if (font::sizescale != SIZESCALE)
196 fatal("sizescale must be equal to %1", SIZESCALE);
197 max_drift = font::res/1000; // this is fairly arbitrary
198 preamble();
201 dvi_printer::~dvi_printer()
203 postamble();
207 draw_dvi_printer::draw_dvi_printer()
208 : output_pen_size(-1), fill(FILL_MAX)
212 draw_dvi_printer::~draw_dvi_printer()
217 void dvi_printer::out1(int n)
219 byte_count += 1;
220 putc(n & 0xff, fp);
223 void dvi_printer::out2(int n)
225 byte_count += 2;
226 putc((n >> 8) & 0xff, fp);
227 putc(n & 0xff, fp);
230 void dvi_printer::out3(int n)
232 byte_count += 3;
233 putc((n >> 16) & 0xff, fp);
234 putc((n >> 8) & 0xff, fp);
235 putc(n & 0xff, fp);
238 void dvi_printer::out4(int n)
240 byte_count += 4;
241 putc((n >> 24) & 0xff, fp);
242 putc((n >> 16) & 0xff, fp);
243 putc((n >> 8) & 0xff, fp);
244 putc(n & 0xff, fp);
247 void dvi_printer::out_string(const char *s)
249 out1(strlen(s));
250 while (*s != 0)
251 out1(*s++);
255 void dvi_printer::end_of_line()
257 if (pushed) {
258 out1(pop);
259 pushed = 0;
260 cur_h = pushed_h;
261 cur_v = pushed_v;
265 void dvi_printer::possibly_begin_line()
267 if (!pushed) {
268 have_pushed = pushed = 1;
269 pushed_h = cur_h;
270 pushed_v = cur_v;
271 out1(push);
275 int scale(int x, int z)
277 int sw;
278 int a, b, c, d;
279 int alpha, beta;
280 alpha = 16*z; beta = 16;
281 while (z >= 040000000L) {
282 z /= 2; beta /= 2;
284 d = x & 255;
285 c = (x >> 8) & 255;
286 b = (x >> 16) & 255;
287 a = (x >> 24) & 255;
288 sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
289 if (a == 255)
290 sw -= alpha;
291 else
292 assert(a == 0);
293 return sw;
297 void dvi_printer::set_char(int index, font *f, const environment *env, int w)
299 unsigned char code = f->get_code(index);
300 if (env->size != cur_point_size || f != cur_font) {
301 cur_font = f;
302 cur_point_size = env->size;
303 for (int i = 0;; i++) {
304 if (i >= FONTS_MAX) {
305 fatal("too many output fonts required");
307 if (output_font_table[i].f == 0) {
308 output_font_table[i].f = (dvi_font *)cur_font;
309 output_font_table[i].point_size = cur_point_size;
310 define_font(i);
312 if (output_font_table[i].f == cur_font
313 && output_font_table[i].point_size == cur_point_size)
314 break;
316 set_font(i);
318 int distance = env->hpos - cur_h;
319 if (env->hpos != end_h && distance != 0) {
320 out_signed(right1, distance);
321 cur_h = env->hpos;
323 else if (distance > max_drift) {
324 out_signed(right1, distance - max_drift);
325 cur_h = env->hpos - max_drift;
327 else if (distance < -max_drift) {
328 out_signed(right1, distance + max_drift);
329 cur_h = env->hpos + max_drift;
331 if (env->vpos != cur_v) {
332 out_signed(down1, env->vpos - cur_v);
333 cur_v = env->vpos;
335 possibly_begin_line();
336 end_h = env->hpos + w;
337 cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER,
338 cur_point_size*RES_7227);
339 if (cur_h > max_h)
340 max_h = cur_h;
341 if (cur_v > max_v)
342 max_v = cur_v;
343 if (code <= 127)
344 out1(code);
345 else
346 out_unsigned(set1, code);
349 void dvi_printer::define_font(int i)
351 out_unsigned(fnt_def1, i);
352 dvi_font *f = output_font_table[i].f;
353 out4(f->checksum);
354 out4(output_font_table[i].point_size*RES_7227);
355 out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
356 const char *nm = f->get_internal_name();
357 out1(0);
358 out_string(nm);
361 void dvi_printer::set_font(int i)
363 if (i >= 0 && i <= 63)
364 out1(fnt_num_0 + i);
365 else
366 out_unsigned(fnt1, i);
369 void dvi_printer::out_signed(unsigned char base, int param)
371 if (-128 <= param && param < 128) {
372 out1(base);
373 out1(param);
375 else if (-32768 <= param && param < 32768) {
376 out1(base+1);
377 out2(param);
379 else if (-(1 << 23) <= param && param < (1 << 23)) {
380 out1(base+2);
381 out3(param);
383 else {
384 out1(base+3);
385 out4(param);
389 void dvi_printer::out_unsigned(unsigned char base, int param)
391 if (param >= 0) {
392 if (param < 256) {
393 out1(base);
394 out1(param);
396 else if (param < 65536) {
397 out1(base+1);
398 out2(param);
400 else if (param < (1 << 24)) {
401 out1(base+2);
402 out3(param);
404 else {
405 out1(base+3);
406 out4(param);
409 else {
410 out1(base+3);
411 out4(param);
415 void dvi_printer::preamble()
417 out1(pre);
418 out1(id_byte);
419 out4(254000);
420 out4(font::res);
421 out4(1000);
422 out1(0);
425 void dvi_printer::postamble()
427 int tem = byte_count;
428 out1(post);
429 out4(last_bop);
430 out4(254000);
431 out4(font::res);
432 out4(1000);
433 out4(max_v);
434 out4(max_h);
435 out2(have_pushed); // stack depth
436 out2(page_count);
437 int i;
438 for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
439 define_font(i);
440 out1(post_post);
441 out4(tem);
442 out1(id_byte);
443 for (i = 0; i < 4 || byte_count % 4 != 0; i++)
444 out1(filler);
447 void dvi_printer::begin_page(int i)
449 page_count++;
450 int tem = byte_count;
451 out1(bop);
452 out4(i);
453 for (int j = 1; j < 10; j++)
454 out4(0);
455 out4(last_bop);
456 last_bop = tem;
457 // By convention position (0,0) in a dvi file is placed at (1in, 1in).
458 cur_h = font::res;
459 cur_v = font::res;
460 end_h = 0;
463 void dvi_printer::end_page()
465 if (pushed)
466 end_of_line();
467 out1(eop);
468 cur_font = 0;
471 void draw_dvi_printer::end_page()
473 dvi_printer::end_page();
474 output_pen_size = -1;
477 void dvi_printer::do_special(const char *s)
479 int len = strlen(s);
480 if (len == 0)
481 return;
482 possibly_begin_line();
483 out_unsigned(xxx1, len);
484 while (*s)
485 out1(*s++);
488 void dvi_printer::special(char *arg, const environment *env)
490 moveto(env->hpos, env->vpos);
491 do_special(arg);
494 void dvi_printer::moveto(int h, int v)
496 if (h != cur_h) {
497 out_signed(right1, h - cur_h);
498 cur_h = h;
499 if (cur_h > max_h)
500 max_h = cur_h;
502 if (v != cur_v) {
503 out_signed(down1, v - cur_v);
504 cur_v = v;
505 if (cur_v > max_v)
506 max_v = cur_v;
508 end_h = 0;
511 void dvi_printer::draw(int code, int *p, int np, const environment *env)
513 if (code == 'l') {
514 int x, y;
515 int height = 0, width;
516 int thickness;
517 if (line_thickness < 0)
518 thickness = env->size*RES_7227*linewidth/1000;
519 else if (line_thickness > 0)
520 thickness = line_thickness;
521 else
522 thickness = 1;
523 if (np != 2) {
524 error("2 arguments required for line");
526 else if (p[0] == 0) {
527 // vertical rule
528 if (p[1] > 0) {
529 x = env->hpos - thickness/2;
530 y = env->vpos + p[1] + thickness/2;
531 height = p[1] + thickness;
532 width = thickness;
534 else if (p[1] < 0) {
535 x = env->hpos - thickness/2;
536 y = env->vpos + thickness/2;
537 height = thickness - p[1];
538 width = thickness;
541 else if (p[1] == 0) {
542 if (p[0] > 0) {
543 x = env->hpos - thickness/2;
544 y = env->vpos + thickness/2;
545 height = thickness;
546 width = p[0] + thickness;
548 else if (p[0] < 0) {
549 x = env->hpos - p[0] - thickness/2;
550 y = env->vpos + thickness/2;
551 height = thickness;
552 width = thickness - p[0];
555 if (height != 0) {
556 moveto(x, y);
557 out1(put_rule);
558 out4(height);
559 out4(width);
562 else if (code == 't') {
563 if (np == 0) {
564 line_thickness = -1;
566 else {
567 // troff gratuitously adds an extra 0
568 if (np != 1 && np != 2)
569 error("0 or 1 argument required for thickness");
570 else
571 line_thickness = p[0];
574 else if (code == 'R') {
575 if (np != 2)
576 error("2 arguments required for rule");
577 else if (p[0] != 0 || p[1] != 0) {
578 int dh = p[0];
579 int dv = p[1];
580 int oh = env->hpos;
581 int ov = env->vpos;
582 if (dv > 0) {
583 ov += dv;
584 dv = -dv;
586 if (dh < 0) {
587 oh += dh;
588 dh = -dh;
590 moveto(oh, ov);
591 out1(put_rule);
592 out4(-dv);
593 out4(dh);
598 // XXX Will this overflow?
600 inline int milliinches(int n)
602 return (n*1000 + font::res/2)/font::res;
605 void draw_dvi_printer::set_line_thickness(const environment *env)
607 int desired_pen_size
608 = milliinches(line_thickness < 0
609 // Will this overflow?
610 ? env->size*RES_7227*linewidth/1000
611 : line_thickness);
612 if (desired_pen_size != output_pen_size) {
613 char buf[256];
614 sprintf(buf, "pn %d", desired_pen_size);
615 do_special(buf);
616 output_pen_size = desired_pen_size;
620 void draw_dvi_printer::fill_next()
622 char buf[256];
623 sprintf(buf, "sh %.3f", double(fill)/FILL_MAX);
624 do_special(buf);
627 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
629 char buf[1024];
630 int fill_flag = 0;
631 switch (code) {
632 case 'C':
633 fill_flag = 1;
634 // fall through
635 case 'c':
636 // troff adds an extra argument to C
637 if (np != 1 && !(code == 'C' && np == 2)) {
638 error("1 argument required for circle");
639 break;
641 moveto(env->hpos+p[0]/2, env->vpos);
642 if (fill_flag)
643 fill_next();
644 else
645 set_line_thickness(env);
646 int rad = milliinches(p[0]/2);
647 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
648 (fill_flag ? "ia" : "ar"),
649 rad,
650 rad);
651 do_special(buf);
652 break;
653 case 'l':
654 if (np != 2) {
655 error("2 arguments required for line");
656 break;
658 moveto(env->hpos, env->vpos);
659 set_line_thickness(env);
660 do_special("pa 0 0");
661 sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
662 do_special(buf);
663 do_special("fp");
664 break;
665 case 'E':
666 fill_flag = 1;
667 // fall through
668 case 'e':
669 if (np != 2) {
670 error("2 arguments required for ellipse");
671 break;
673 moveto(env->hpos+p[0]/2, env->vpos);
674 if (fill_flag)
675 fill_next();
676 sprintf(buf, "%s 0 0 %d %d 0 6.28319",
677 (fill_flag ? "ia" : "ar"),
678 milliinches(p[0]/2),
679 milliinches(p[1]/2));
680 do_special(buf);
681 break;
682 case 'P':
683 fill_flag = 1;
684 // fall through
685 case 'p':
687 if (np & 1) {
688 error("even number of arguments required for polygon");
689 break;
691 if (np == 0) {
692 error("no arguments for polygon");
693 break;
695 moveto(env->hpos, env->vpos);
696 if (fill_flag)
697 fill_next();
698 else
699 set_line_thickness(env);
700 do_special("pa 0 0");
701 int h = 0, v = 0;
702 for (int i = 0; i < np; i += 2) {
703 h += p[i];
704 v += p[i+1];
705 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
706 do_special(buf);
708 do_special("pa 0 0");
709 do_special(fill_flag ? "ip" : "fp");
710 break;
712 case '~':
714 if (np & 1) {
715 error("even number of arguments required for spline");
716 break;
718 if (np == 0) {
719 error("no arguments for spline");
720 break;
722 moveto(env->hpos, env->vpos);
723 set_line_thickness(env);
724 do_special("pa 0 0");
725 int h = 0, v = 0;
726 for (int i = 0; i < np; i += 2) {
727 h += p[i];
728 v += p[i+1];
729 sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
730 do_special(buf);
732 do_special("sp");
733 break;
735 case 'a':
737 if (np != 4) {
738 error("4 arguments required for arc");
739 break;
741 set_line_thickness(env);
742 int ch = p[0];
743 int cv = p[1];
744 int eh = p[0] + p[2];
745 int ev = p[1] + p[3];
746 double n = (double(ch)*eh) + (double(cv)*ev);
747 if (n == 0) {
748 moveto(env->hpos, env->vpos);
749 do_special("pa 0 0");
750 sprintf(buf, "pa %d %d", milliinches(eh), milliinches(ev));
751 do_special(buf);
752 do_special("fp");
754 else {
755 double k = (double(eh)*eh + double(ev)*ev)/(2.0*n);
756 double h = k*ch;
757 double v = k*cv;
758 double start_angle = atan2(-v, -h);
759 double end_angle = atan2(ev - v, eh - h);
760 int rad = milliinches(int(sqrt(h*h + v*v) + .5));
761 moveto(env->hpos + int(h), env->vpos + int(v));
762 sprintf(buf, "ar 0 0 %d %d %f %f",
763 rad,
764 rad,
765 end_angle,
766 start_angle);
767 do_special(buf);
769 break;
771 case 't':
773 if (np == 0) {
774 line_thickness = -1;
776 else {
777 // troff gratuitously adds an extra 0
778 if (np != 1 && np != 2) {
779 error("0 or 1 argument required for thickness");
780 break;
782 line_thickness = p[0];
784 break;
786 case 'f':
788 if (np != 1 && np != 2) {
789 error("1 argument required for fill");
790 break;
792 fill = p[0];
793 if (fill < 0 || fill > FILL_MAX)
794 fill = FILL_MAX;
795 break;
797 case 'R':
799 if (np != 2) {
800 error("2 arguments required for rule");
801 break;
803 int dh = p[0];
804 if (dh == 0)
805 break;
806 int dv = p[1];
807 if (dv == 0)
808 break;
809 int oh = env->hpos;
810 int ov = env->vpos;
811 if (dv > 0) {
812 ov += dv;
813 dv = -dv;
815 if (dh < 0) {
816 oh += dh;
817 dh = -dh;
819 moveto(oh, ov);
820 out1(put_rule);
821 out4(-dv);
822 out4(dh);
823 break;
825 default:
826 error("unrecognised drawing command `%1'", char(code));
827 break;
831 font *dvi_printer::make_font(const char *nm)
833 return dvi_font::load_dvi_font(nm);
836 printer *make_printer()
838 if (draw_flag)
839 return new draw_dvi_printer;
840 else
841 return new dvi_printer;
844 static void usage();
846 int main(int argc, char **argv)
848 program_name = argv[0];
849 static char stderr_buf[BUFSIZ];
850 setbuf(stderr, stderr_buf);
851 int c;
852 while ((c = getopt(argc, argv, "F:vw:d")) != EOF)
853 switch(c) {
854 case 'v':
856 extern const char *version_string;
857 fprintf(stderr, "grodvi version %s\n", version_string);
858 fflush(stderr);
859 break;
861 case 'w':
862 if (sscanf(optarg, "%d", &linewidth) != 1
863 || linewidth < 0 || linewidth > 1000) {
864 error("bad line width");
865 linewidth = DEFAULT_LINEWIDTH;
867 break;
868 case 'd':
869 draw_flag = 0;
870 break;
871 case 'F':
872 font::command_line_font_dir(optarg);
873 break;
874 case '?':
875 usage();
876 break;
877 default:
878 assert(0);
880 if (optind >= argc)
881 do_file("-");
882 else {
883 for (int i = optind; i < argc; i++)
884 do_file(argv[i]);
886 delete pr;
887 exit(0);
890 static void usage()
892 fprintf(stderr, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
893 program_name);
894 exit(1);