Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / devices / grodvi / dvi.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
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
11 version.
12
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
16 for more details.
17
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22 #include "driver.h"
23 #include "nonposix.h"
24 #include "paper.h"
25
26 extern "C" const char *Version_string;
27
28 #define DEFAULT_LINEWIDTH 40
29 static int linewidth = DEFAULT_LINEWIDTH;
30
31 static int draw_flag = 1;
32
33 static int landscape_flag = 0;
34 static double user_paper_length = 0;
35 static double user_paper_width = 0;
36
37 /* These values were chosen because:
38
39 (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
40
41 and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
42
43 The width in the groff font file is the product of MULTIPLIER and the
44 width in the tfm file. */
45
46 #define RES 57816
47 #define RES_7227 (RES/7227)
48 #define UNITWIDTH 131072
49 #define SIZESCALE 100
50 #define MULTIPLIER 1
51
52 class dvi_font : public font {
53   dvi_font(const char *);
54 public:
55   int checksum;
56   int design_size;
57   ~dvi_font();
58   void handle_unknown_font_command(const char *command, const char *arg,
59                                    const char *filename, int lineno);
60   static dvi_font *load_dvi_font(const char *);
61 };
62
63 dvi_font *dvi_font::load_dvi_font(const char *s)
64 {
65   dvi_font *f = new dvi_font(s);
66   if (!f->load()) {
67     delete f;
68     return 0;
69   }
70   return f;
71 }
72
73 dvi_font::dvi_font(const char *nm)
74 : font(nm), checksum(0), design_size(0)
75 {
76 }
77
78 dvi_font::~dvi_font()
79 {
80 }
81
82 void dvi_font::handle_unknown_font_command(const char *command,
83                                            const char *arg,
84                                            const char *filename, int lineno)
85 {
86   char *ptr;
87   if (strcmp(command, "checksum") == 0) {
88     if (arg == 0)
89       fatal_with_file_and_line(filename, lineno,
90                                "`checksum' command requires an argument");
91     checksum = int(strtol(arg, &ptr, 10));
92     if (checksum == 0 && ptr == arg) {
93       fatal_with_file_and_line(filename, lineno, "bad checksum");
94     }
95   }
96   else if (strcmp(command, "designsize") == 0) {
97     if (arg == 0)
98       fatal_with_file_and_line(filename, lineno,
99                                "`designsize' command requires an argument");
100     design_size = int(strtol(arg, &ptr, 10));
101     if (design_size == 0 && ptr == arg) {
102       fatal_with_file_and_line(filename, lineno, "bad design size");
103     }
104   }
105 }
106
107 #define FONTS_MAX 256
108
109 struct output_font {
110   dvi_font *f;
111   int point_size;
112   output_font() : f(0) { }
113 };
114
115 class dvi_printer : public printer {
116   FILE *fp;
117   int max_drift;
118   int byte_count;
119   int last_bop;
120   int page_count;
121   int cur_h;
122   int cur_v;
123   int end_h;
124   int max_h;
125   int max_v;
126   output_font output_font_table[FONTS_MAX];
127   font *cur_font;
128   int cur_point_size;
129   color cur_color;
130   int pushed;
131   int pushed_h;
132   int pushed_v;
133   int have_pushed;
134   void preamble();
135   void postamble();
136   void define_font(int);
137   void set_font(int);
138   void possibly_begin_line();
139   void set_color(color *);
140 protected:
141   enum {
142     id_byte = 2,
143     set1 = 128,
144     put1 = 133,
145     put_rule = 137,
146     bop = 139,
147     eop = 140,
148     push = 141,
149     pop = 142,
150     right1 = 143,
151     down1 = 157,
152     fnt_num_0 = 171,
153     fnt1 = 235,
154     xxx1 = 239,
155     fnt_def1 = 243,
156     pre = 247,
157     post = 248,
158     post_post = 249,
159     filler = 223
160   };
161   int line_thickness;
162
163   void out1(int);
164   void out2(int);
165   void out3(int);
166   void out4(int);
167   void moveto(int, int);
168   void out_string(const char *);
169   void out_signed(unsigned char, int);
170   void out_unsigned(unsigned char, int);
171   void do_special(const char *);
172 public:
173   dvi_printer();
174   ~dvi_printer();
175   font *make_font(const char *);
176   void begin_page(int);
177   void end_page(int);
178   void set_char(int, font *, const environment *, int w, const char *name);
179   void special(char *arg, const environment *env, char type);
180   void end_of_line();
181   void draw(int code, int *p, int np, const environment *env);
182 };
183
184
185 class draw_dvi_printer : public dvi_printer {
186   int output_pen_size;
187   void set_line_thickness(const environment *);
188   void fill_next(const environment *);
189 public:
190   draw_dvi_printer();
191   ~draw_dvi_printer();
192   void draw(int code, int *p, int np, const environment *env);
193   void end_page(int);
194 };
195
196 dvi_printer::dvi_printer()
197 : fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
198   cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
199 {
200   if (font::res != RES)
201     fatal("resolution must be %1", RES);
202   if (font::unitwidth != UNITWIDTH)
203     fatal("unitwidth must be %1", UNITWIDTH);
204   if (font::hor != 1)
205     fatal("hor must be equal to 1");
206   if (font::vert != 1)
207     fatal("vert must be equal to 1");
208   if (font::sizescale != SIZESCALE)
209     fatal("sizescale must be equal to %1", SIZESCALE);
210   max_drift = font::res/1000;   // this is fairly arbitrary
211   preamble();
212 }
213
214 dvi_printer::~dvi_printer()
215 {
216   postamble();
217 }
218
219
220 draw_dvi_printer::draw_dvi_printer()
221 : output_pen_size(-1)
222 {
223 }
224
225 draw_dvi_printer::~draw_dvi_printer()
226 {
227 }
228
229
230 void dvi_printer::out1(int n)
231 {
232   byte_count += 1;
233   putc(n & 0xff, fp);
234 }
235
236 void dvi_printer::out2(int n)
237 {
238   byte_count += 2;
239   putc((n >> 8) & 0xff, fp);
240   putc(n & 0xff, fp);
241 }
242
243 void dvi_printer::out3(int n)
244 {
245   byte_count += 3;
246   putc((n >> 16) & 0xff, fp);
247   putc((n >> 8) & 0xff, fp);
248   putc(n & 0xff, fp);
249 }
250
251 void dvi_printer::out4(int n)
252 {
253   byte_count += 4;
254   putc((n >> 24) & 0xff, fp);
255   putc((n >> 16) & 0xff, fp);
256   putc((n >> 8) & 0xff, fp);
257   putc(n & 0xff, fp);
258 }
259
260 void dvi_printer::out_string(const char *s)
261 {
262   out1(strlen(s));
263   while (*s != 0)
264     out1(*s++);
265 }
266
267
268 void dvi_printer::end_of_line()
269 {
270   if (pushed) {
271     out1(pop);
272     pushed = 0;
273     cur_h = pushed_h;
274     cur_v = pushed_v;
275   }
276 }
277
278 void dvi_printer::possibly_begin_line()
279 {
280   if (!pushed) {
281     have_pushed = pushed = 1;
282     pushed_h = cur_h;
283     pushed_v = cur_v;
284     out1(push);
285   }
286 }
287
288 int scale(int x, int z)
289 {
290   int sw;
291   int a, b, c, d;
292   int alpha, beta;
293   alpha = 16*z; beta = 16;
294   while (z >= 040000000L) {
295     z /= 2; beta /= 2;
296   }
297   d = x & 255;
298   c = (x >> 8) & 255;
299   b = (x >> 16) & 255;
300   a = (x >> 24) & 255;
301   sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
302   if (a == 255)
303     sw -= alpha;
304   else
305     assert(a == 0);
306   return sw;
307 }
308
309 void dvi_printer::set_color(color *col)
310 {
311   cur_color = *col;
312   char buf[256];
313   unsigned int components[4];
314   color_scheme cs = col->get_components(components);
315   switch (cs) {
316   case DEFAULT:
317     sprintf(buf, "color gray 0");
318     break;
319   case RGB:
320     sprintf(buf, "color rgb %.3g %.3g %.3g",
321                  double(Red) / color::MAX_COLOR_VAL,
322                  double(Green) / color::MAX_COLOR_VAL,
323                  double(Blue) / color::MAX_COLOR_VAL);
324     break;
325   case CMY:
326     col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
327     // fall through
328   case CMYK:
329     sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
330                  double(Cyan) / color::MAX_COLOR_VAL,
331                  double(Magenta) / color::MAX_COLOR_VAL,
332                  double(Yellow) / color::MAX_COLOR_VAL,
333                  double(Black) / color::MAX_COLOR_VAL);
334     break;
335   case GRAY:
336     sprintf(buf, "color gray %.3g",
337                  double(Gray) / color::MAX_COLOR_VAL);
338     break;
339   }
340   do_special(buf);
341 }
342
343 void dvi_printer::set_char(int idx, font *f, const environment *env,
344                            int w, const char *)
345 {
346   if (*env->col != cur_color)
347     set_color(env->col);
348   int code = f->get_code(idx);
349   if (env->size != cur_point_size || f != cur_font) {
350     cur_font = f;
351     cur_point_size = env->size;
352     int i;
353     for (i = 0;; i++) {
354       if (i >= FONTS_MAX) {
355         fatal("too many output fonts required");
356       }
357       if (output_font_table[i].f == 0) {
358         output_font_table[i].f = (dvi_font *)cur_font;
359         output_font_table[i].point_size = cur_point_size;
360         define_font(i);
361       }
362       if (output_font_table[i].f == cur_font
363           && output_font_table[i].point_size == cur_point_size)
364         break;
365     }
366     set_font(i);
367   }
368   int distance = env->hpos - cur_h;
369   if (env->hpos != end_h && distance != 0) {
370     out_signed(right1, distance);
371     cur_h = env->hpos;
372   }
373   else if (distance > max_drift) {
374     out_signed(right1, distance - max_drift);
375     cur_h = env->hpos - max_drift;
376   }
377   else if (distance < -max_drift) {
378     out_signed(right1, distance + max_drift);
379     cur_h = env->hpos + max_drift;
380   }
381   if (env->vpos != cur_v) {
382     out_signed(down1, env->vpos - cur_v);
383     cur_v = env->vpos;
384   }
385   possibly_begin_line();
386   end_h = env->hpos + w;
387   cur_h += scale(f->get_width(idx, UNITWIDTH)/MULTIPLIER,
388                  cur_point_size*RES_7227);
389   if (cur_h > max_h)
390     max_h = cur_h;
391   if (cur_v > max_v)
392     max_v = cur_v;
393   if (code >= 0 && code <= 127)
394     out1(code);
395   else
396     out_unsigned(set1, code);
397 }
398
399 void dvi_printer::define_font(int i)
400 {
401   out_unsigned(fnt_def1, i);
402   dvi_font *f = output_font_table[i].f;
403   out4(f->checksum);
404   out4(output_font_table[i].point_size*RES_7227);
405   out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
406   const char *nm = f->get_internal_name();
407   out1(0);
408   out_string(nm);
409 }
410
411 void dvi_printer::set_font(int i)
412 {
413   if (i >= 0 && i <= 63)
414     out1(fnt_num_0 + i);
415   else
416     out_unsigned(fnt1, i);
417 }
418
419 void dvi_printer::out_signed(unsigned char base, int param)
420 {
421   if (-128 <= param && param < 128) {
422     out1(base);
423     out1(param);
424   }
425   else if (-32768 <= param && param < 32768) {
426     out1(base+1);
427     out2(param);
428   }
429   else if (-(1 << 23) <= param && param < (1 << 23)) {
430     out1(base+2);
431     out3(param);
432   }
433   else {
434     out1(base+3);
435     out4(param);
436   }
437 }
438
439 void dvi_printer::out_unsigned(unsigned char base, int param)
440 {
441   if (param >= 0) {
442     if (param < 256) {
443       out1(base);
444       out1(param);
445     }
446     else if (param < 65536) {
447       out1(base+1);
448       out2(param);
449     }
450     else if (param < (1 << 24)) {
451       out1(base+2);
452       out3(param);
453     }
454     else {
455       out1(base+3);
456       out4(param);
457     }
458   }
459   else {
460     out1(base+3);
461     out4(param);
462   }
463 }
464
465 void dvi_printer::preamble()
466 {
467   out1(pre);
468   out1(id_byte);
469   out4(254000);
470   out4(font::res);
471   out4(1000);
472   out1(0);
473 }
474
475 void dvi_printer::postamble()
476 {
477   int tem = byte_count;
478   out1(post);
479   out4(last_bop);
480   out4(254000);
481   out4(font::res);
482   out4(1000);
483   out4(max_v);
484   out4(max_h);
485   out2(have_pushed); // stack depth
486   out2(page_count);
487   int i;
488   for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
489     define_font(i);
490   out1(post_post);
491   out4(tem);
492   out1(id_byte);
493   for (i = 0; i < 4 || byte_count % 4 != 0; i++)
494     out1(filler);
495 }  
496   
497 void dvi_printer::begin_page(int i)
498 {
499   page_count++;
500   int tem = byte_count;
501   out1(bop);
502   out4(i);
503   for (int j = 1; j < 10; j++)
504     out4(0);
505   out4(last_bop);
506   last_bop = tem;
507   // By convention position (0,0) in a dvi file is placed at (1in, 1in).
508   cur_h = font::res;
509   cur_v = font::res;
510   end_h = 0;
511   if (page_count == 1) {
512     char buf[256];
513     // at least dvips uses this
514     double length = user_paper_length ? user_paper_length :
515                                         double(font::paperlength) / font::res;
516     double width = user_paper_width ? user_paper_width :
517                                       double(font::paperwidth) / font::res;
518     if (width > 0 && length > 0) {
519       sprintf(buf, "papersize=%.3fin,%.3fin",
520               landscape_flag ? length : width,
521               landscape_flag ? width : length);
522       do_special(buf);
523     }
524   }
525   if (cur_color != default_color)
526     set_color(&cur_color);
527 }
528
529 void dvi_printer::end_page(int)
530 {
531   set_color(&default_color);
532   if (pushed)
533     end_of_line();
534   out1(eop);
535   cur_font = 0;
536 }
537
538 void draw_dvi_printer::end_page(int len)
539 {
540   dvi_printer::end_page(len);
541   output_pen_size = -1;
542 }
543
544 void dvi_printer::do_special(const char *s)
545 {
546   int len = strlen(s);
547   if (len == 0)
548     return;
549   possibly_begin_line();
550   out_unsigned(xxx1, len);
551   while (*s)
552     out1(*s++);
553 }
554
555 void dvi_printer::special(char *arg, const environment *env, char type)
556 {
557   if (type != 'p')
558     return;
559   moveto(env->hpos, env->vpos);
560   do_special(arg);
561 }
562
563 void dvi_printer::moveto(int h, int v)
564 {
565   if (h != cur_h) {
566     out_signed(right1, h - cur_h);
567     cur_h = h;
568     if (cur_h > max_h)
569       max_h = cur_h;
570   }
571   if (v != cur_v) {
572     out_signed(down1, v - cur_v);
573     cur_v = v;
574     if (cur_v > max_v)
575       max_v = cur_v;
576   }
577   end_h = 0;
578 }
579
580 void dvi_printer::draw(int code, int *p, int np, const environment *env)
581 {
582   if (code == 'l') {
583     int x = 0, y = 0;
584     int height = 0, width = 0;
585     int thickness;
586     if (line_thickness < 0)
587       thickness = env->size*RES_7227*linewidth/1000;
588     else if (line_thickness > 0)
589       thickness = line_thickness;
590     else
591       thickness = 1;
592     if (np != 2) {
593       error("2 arguments required for line");
594     }
595     else if (p[0] == 0) {
596       // vertical rule
597       if (p[1] > 0) {
598         x = env->hpos - thickness/2;
599         y = env->vpos + p[1] + thickness/2;
600         height = p[1] + thickness;
601         width = thickness;
602       }
603       else if (p[1] < 0) {
604         x = env->hpos - thickness/2;
605         y = env->vpos + thickness/2;
606         height = thickness - p[1];
607         width = thickness;
608       }
609     }
610     else if (p[1] == 0) {
611       if (p[0] > 0) {
612         x = env->hpos - thickness/2;
613         y = env->vpos + thickness/2;
614         height = thickness;
615         width = p[0] + thickness;
616       }
617       else if (p[0] < 0) {
618         x = env->hpos - p[0] - thickness/2;
619         y = env->vpos + thickness/2;
620         height = thickness;
621         width = thickness - p[0];
622       }
623     }
624     if (height != 0) {
625       moveto(x, y);
626       out1(put_rule);
627       out4(height);
628       out4(width);
629     }
630   }
631   else if (code == 't') {
632     if (np == 0) {
633       line_thickness = -1;
634     }
635     else {
636       // troff gratuitously adds an extra 0
637       if (np != 1 && np != 2)
638         error("0 or 1 argument required for thickness");
639       else
640         line_thickness = p[0];
641     }
642   }
643   else if (code == 'R') {
644     if (np != 2)
645       error("2 arguments required for rule");
646     else if (p[0] != 0 || p[1] != 0) {
647       int dh = p[0];
648       int dv = p[1];
649       int oh = env->hpos;
650       int ov = env->vpos;
651       if (dv > 0) {
652         ov += dv;
653         dv = -dv;
654       }
655       if (dh < 0) {
656         oh += dh;
657         dh = -dh;
658       }
659       moveto(oh, ov);
660       out1(put_rule);
661       out4(-dv);
662       out4(dh);
663     }
664   }
665 }
666
667 // XXX Will this overflow?
668
669 inline int milliinches(int n)
670 {
671   return (n*1000 + font::res/2)/font::res;
672 }
673
674 void draw_dvi_printer::set_line_thickness(const environment *env)
675 {
676   int desired_pen_size
677     = milliinches(line_thickness < 0
678                   // Will this overflow?
679                   ? env->size*RES_7227*linewidth/1000
680                   : line_thickness);
681   if (desired_pen_size != output_pen_size) {
682     char buf[256];
683     sprintf(buf, "pn %d", desired_pen_size);
684     do_special(buf);
685     output_pen_size = desired_pen_size;
686   }
687 }
688
689 void draw_dvi_printer::fill_next(const environment *env)
690 {
691   unsigned int g;
692   if (env->fill->is_default())
693     g = 0;
694   else {
695     // currently, only BW support
696     env->fill->get_gray(&g);
697   }
698   char buf[256];
699   sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
700   do_special(buf);
701 }
702
703 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
704 {
705   char buf[1024];
706   int fill_flag = 0;
707   switch (code) {
708   case 'C':
709     fill_flag = 1;
710     // fall through
711   case 'c':
712     {
713       // troff adds an extra argument to C
714       if (np != 1 && !(code == 'C' && np == 2)) {
715         error("1 argument required for circle");
716         break;
717       }
718       moveto(env->hpos+p[0]/2, env->vpos);
719       if (fill_flag)
720         fill_next(env);
721       else
722         set_line_thickness(env);
723       int rad;
724       rad = milliinches(p[0]/2);
725       sprintf(buf, "%s 0 0 %d %d 0 6.28319",
726               (fill_flag ? "ia" : "ar"),
727               rad,
728               rad);
729       do_special(buf);
730       break;
731     }
732   case 'l':
733     if (np != 2) {
734       error("2 arguments required for line");
735       break;
736     }
737     moveto(env->hpos, env->vpos);
738     set_line_thickness(env);
739     do_special("pa 0 0");
740     sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
741     do_special(buf);
742     do_special("fp");
743     break;
744   case 'E':
745     fill_flag = 1;
746     // fall through
747   case 'e':
748     if (np != 2) {
749       error("2 arguments required for ellipse");
750       break;
751     }
752     moveto(env->hpos+p[0]/2, env->vpos);
753     if (fill_flag)
754       fill_next(env);
755     sprintf(buf, "%s 0 0 %d %d 0 6.28319",
756             (fill_flag ? "ia" : "ar"),
757             milliinches(p[0]/2),
758             milliinches(p[1]/2));
759     do_special(buf);
760     break;
761   case 'P':
762     fill_flag = 1;
763     // fall through
764   case 'p':
765     {
766       if (np & 1) {
767         error("even number of arguments required for polygon");
768         break;
769       }
770       if (np == 0) {
771         error("no arguments for polygon");
772         break;
773       }
774       moveto(env->hpos, env->vpos);
775       if (fill_flag)
776         fill_next(env);
777       else
778         set_line_thickness(env);
779       do_special("pa 0 0");
780       int h = 0, v = 0;
781       for (int i = 0; i < np; i += 2) {
782         h += p[i];
783         v += p[i+1];
784         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
785         do_special(buf);
786       }
787       do_special("pa 0 0");
788       do_special(fill_flag ? "ip" : "fp");
789       break;
790     }
791   case '~':
792     {
793       if (np & 1) {
794         error("even number of arguments required for spline");
795         break;
796       }
797       if (np == 0) {
798         error("no arguments for spline");
799         break;
800       }
801       moveto(env->hpos, env->vpos);
802       set_line_thickness(env);
803       do_special("pa 0 0");
804       int h = 0, v = 0;
805       for (int i = 0; i < np; i += 2) {
806         h += p[i];
807         v += p[i+1];
808         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
809         do_special(buf);
810       }
811       do_special("sp");
812       break;
813     }
814   case 'a':
815     {
816       if (np != 4) {
817         error("4 arguments required for arc");
818         break;
819       }
820       set_line_thickness(env);
821       double c[2];
822       if (adjust_arc_center(p, c)) {
823         int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
824         moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
825         double start = atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]);
826         double end = atan2(-c[1], -c[0]);
827         if (end - start < 0)
828           start -= 2 * 3.14159265358;
829         sprintf(buf, "ar 0 0 %d %d %f %f", rad, rad, start, end);
830         do_special(buf);
831       }
832       else {
833         moveto(env->hpos, env->vpos);
834         do_special("pa 0 0");
835         sprintf(buf,
836                 "pa %d %d",
837                 milliinches(p[0] + p[2]),
838                 milliinches(p[1] + p[3]));
839         do_special(buf);
840         do_special("fp");
841       }
842       break;
843     }
844   case 't':
845     {
846       if (np == 0) {
847         line_thickness = -1;
848       }
849       else {
850         // troff gratuitously adds an extra 0
851         if (np != 1 && np != 2) {
852           error("0 or 1 argument required for thickness");
853           break;
854         }
855         line_thickness = p[0];
856       }
857       break;
858     }
859   case 'R':
860     {
861       if (np != 2) {
862         error("2 arguments required for rule");
863         break;
864       }
865       int dh = p[0];
866       if (dh == 0)
867         break;
868       int dv = p[1];
869       if (dv == 0)
870         break;
871       int oh = env->hpos;
872       int ov = env->vpos;
873       if (dv > 0) {
874         ov += dv;
875         dv = -dv;
876       }
877       if (dh < 0) {
878         oh += dh;
879         dh = -dh;
880       }
881       moveto(oh, ov);
882       out1(put_rule);
883       out4(-dv);
884       out4(dh);
885       break;
886     }
887   default:
888     error("unrecognised drawing command `%1'", char(code));
889     break;
890   }
891 }
892
893 font *dvi_printer::make_font(const char *nm)
894 {
895   return dvi_font::load_dvi_font(nm);
896 }
897
898 printer *make_printer()
899 {
900   if (draw_flag)
901     return new draw_dvi_printer;
902   else
903     return new dvi_printer;
904 }
905
906 static void usage(FILE *stream);
907
908 int main(int argc, char **argv)
909 {
910   setlocale(LC_NUMERIC, "C");
911   program_name = argv[0];
912   static char stderr_buf[BUFSIZ];
913   setbuf(stderr, stderr_buf);
914   int c;
915   static const struct option long_options[] = {
916     { "help", no_argument, 0, CHAR_MAX + 1 },
917     { "version", no_argument, 0, 'v' },
918     { NULL, 0, 0, 0 }
919   };
920   while ((c = getopt_long(argc, argv, "dF:I:lp:vw:", long_options, NULL))
921          != EOF)
922     switch(c) {
923     case 'd':
924       draw_flag = 0;
925       break;
926     case 'l':
927       landscape_flag = 1;
928       break;
929     case 'F':
930       font::command_line_font_dir(optarg);
931       break;
932     case 'I':
933       // ignore include search path
934       break;
935     case 'p':
936       if (!font::scan_papersize(optarg, 0,
937                                 &user_paper_length, &user_paper_width))
938         error("invalid custom paper size `%1' ignored", optarg);
939       break;
940     case 'v':
941       {
942         printf("GNU grodvi (groff) version %s\n", Version_string);
943         exit(0);
944         break;
945       }
946     case 'w':
947       if (sscanf(optarg, "%d", &linewidth) != 1
948           || linewidth < 0 || linewidth > 1000) {
949         error("bad line width");
950         linewidth = DEFAULT_LINEWIDTH;
951       }
952       break;
953     case CHAR_MAX + 1: // --help
954       usage(stdout);
955       exit(0);
956       break;
957     case '?':
958       usage(stderr);
959       exit(1);
960       break;
961     default:
962       assert(0);
963     }
964   SET_BINARY(fileno(stdout));
965   if (optind >= argc)
966     do_file("-");
967   else {
968     for (int i = optind; i < argc; i++)
969       do_file(argv[i]);
970   }
971   return 0;
972 }
973
974 static void usage(FILE *stream)
975 {
976   fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
977           program_name);
978 }