6bcc64f6a35044673d285da7bb3c8f76bfb840c0
[platform/upstream/groff.git] / src / devices / grodvi / dvi.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2014  Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
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 3 of the License, or
10 (at your option) any later version.
11
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.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "driver.h"
21 #include "nonposix.h"
22 #include "paper.h"
23
24 extern "C" const char *Version_string;
25
26 #define DEFAULT_LINEWIDTH 40
27 static int linewidth = DEFAULT_LINEWIDTH;
28
29 static int draw_flag = 1;
30
31 static int landscape_flag = 0;
32 static double user_paper_length = 0;
33 static double user_paper_width = 0;
34
35 /* These values were chosen because:
36
37 (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
38
39 and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
40
41 The width in the groff font file is the product of MULTIPLIER and the
42 width in the tfm file. */
43
44 #define RES 57816
45 #define RES_7227 (RES/7227)
46 #define UNITWIDTH 131072
47 #define SIZESCALE 100
48 #define MULTIPLIER 1
49
50 class dvi_font : public font {
51   dvi_font(const char *);
52 public:
53   int checksum;
54   int design_size;
55   ~dvi_font();
56   void handle_unknown_font_command(const char *command, const char *arg,
57                                    const char *filename, int lineno);
58   static dvi_font *load_dvi_font(const char *);
59 };
60
61 dvi_font *dvi_font::load_dvi_font(const char *s)
62 {
63   dvi_font *f = new dvi_font(s);
64   if (!f->load()) {
65     delete f;
66     return 0;
67   }
68   return f;
69 }
70
71 dvi_font::dvi_font(const char *nm)
72 : font(nm), checksum(0), design_size(0)
73 {
74 }
75
76 dvi_font::~dvi_font()
77 {
78 }
79
80 void dvi_font::handle_unknown_font_command(const char *command,
81                                            const char *arg,
82                                            const char *filename, int lineno)
83 {
84   char *ptr;
85   if (strcmp(command, "checksum") == 0) {
86     if (arg == 0)
87       fatal_with_file_and_line(filename, lineno,
88                                "`checksum' command requires an argument");
89     checksum = int(strtol(arg, &ptr, 10));
90     if (checksum == 0 && ptr == arg) {
91       fatal_with_file_and_line(filename, lineno, "bad checksum");
92     }
93   }
94   else if (strcmp(command, "designsize") == 0) {
95     if (arg == 0)
96       fatal_with_file_and_line(filename, lineno,
97                                "`designsize' command requires an argument");
98     design_size = int(strtol(arg, &ptr, 10));
99     if (design_size == 0 && ptr == arg) {
100       fatal_with_file_and_line(filename, lineno, "bad design size");
101     }
102   }
103 }
104
105 #define FONTS_MAX 256
106
107 struct output_font {
108   dvi_font *f;
109   int point_size;
110   output_font() : f(0) { }
111 };
112
113 class dvi_printer : public printer {
114   FILE *fp;
115   int max_drift;
116   int byte_count;
117   int last_bop;
118   int page_count;
119   int cur_h;
120   int cur_v;
121   int end_h;
122   int max_h;
123   int max_v;
124   output_font output_font_table[FONTS_MAX];
125   font *cur_font;
126   int cur_point_size;
127   color cur_color;
128   int pushed;
129   int pushed_h;
130   int pushed_v;
131   int have_pushed;
132   void preamble();
133   void postamble();
134   void define_font(int);
135   void set_font(int);
136   void possibly_begin_line();
137   void set_color(color *);
138 protected:
139   enum {
140     id_byte = 2,
141     set1 = 128,
142     put1 = 133,
143     put_rule = 137,
144     bop = 139,
145     eop = 140,
146     push = 141,
147     pop = 142,
148     right1 = 143,
149     down1 = 157,
150     fnt_num_0 = 171,
151     fnt1 = 235,
152     xxx1 = 239,
153     fnt_def1 = 243,
154     pre = 247,
155     post = 248,
156     post_post = 249,
157     filler = 223
158   };
159   int line_thickness;
160
161   void out1(int);
162   void out2(int);
163   void out3(int);
164   void out4(int);
165   void moveto(int, int);
166   void out_string(const char *);
167   void out_signed(unsigned char, int);
168   void out_unsigned(unsigned char, int);
169   void do_special(const char *);
170 public:
171   dvi_printer();
172   ~dvi_printer();
173   font *make_font(const char *);
174   void begin_page(int);
175   void end_page(int);
176   void set_char(glyph *, font *, const environment *, int, const char *);
177   void special(char *, const environment *, char);
178   void end_of_line();
179   void draw(int, int *, int, const environment *);
180 };
181
182
183 class draw_dvi_printer : public dvi_printer {
184   int output_pen_size;
185   void set_line_thickness(const environment *);
186   void fill_next(const environment *);
187 public:
188   draw_dvi_printer();
189   ~draw_dvi_printer();
190   void draw(int code, int *p, int np, const environment *env);
191   void end_page(int);
192 };
193
194 dvi_printer::dvi_printer()
195 : fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
196   cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
197 {
198   if (font::res != RES)
199     fatal("resolution must be %1", RES);
200   if (font::unitwidth != UNITWIDTH)
201     fatal("unitwidth must be %1", UNITWIDTH);
202   if (font::hor != 1)
203     fatal("hor must be equal to 1");
204   if (font::vert != 1)
205     fatal("vert must be equal to 1");
206   if (font::sizescale != SIZESCALE)
207     fatal("sizescale must be equal to %1", SIZESCALE);
208   max_drift = font::res/1000;   // this is fairly arbitrary
209   preamble();
210 }
211
212 dvi_printer::~dvi_printer()
213 {
214   postamble();
215 }
216
217
218 draw_dvi_printer::draw_dvi_printer()
219 : output_pen_size(-1)
220 {
221 }
222
223 draw_dvi_printer::~draw_dvi_printer()
224 {
225 }
226
227
228 void dvi_printer::out1(int n)
229 {
230   byte_count += 1;
231   putc(n & 0xff, fp);
232 }
233
234 void dvi_printer::out2(int n)
235 {
236   byte_count += 2;
237   putc((n >> 8) & 0xff, fp);
238   putc(n & 0xff, fp);
239 }
240
241 void dvi_printer::out3(int n)
242 {
243   byte_count += 3;
244   putc((n >> 16) & 0xff, fp);
245   putc((n >> 8) & 0xff, fp);
246   putc(n & 0xff, fp);
247 }
248
249 void dvi_printer::out4(int n)
250 {
251   byte_count += 4;
252   putc((n >> 24) & 0xff, fp);
253   putc((n >> 16) & 0xff, fp);
254   putc((n >> 8) & 0xff, fp);
255   putc(n & 0xff, fp);
256 }
257
258 void dvi_printer::out_string(const char *s)
259 {
260   out1(strlen(s));
261   while (*s != 0)
262     out1(*s++);
263 }
264
265
266 void dvi_printer::end_of_line()
267 {
268   if (pushed) {
269     out1(pop);
270     pushed = 0;
271     cur_h = pushed_h;
272     cur_v = pushed_v;
273   }
274 }
275
276 void dvi_printer::possibly_begin_line()
277 {
278   if (!pushed) {
279     have_pushed = pushed = 1;
280     pushed_h = cur_h;
281     pushed_v = cur_v;
282     out1(push);
283   }
284 }
285
286 int scale(int x, int z)
287 {
288   int sw;
289   int a, b, c, d;
290   int alpha, beta;
291   alpha = 16*z; beta = 16;
292   while (z >= 040000000L) {
293     z /= 2; beta /= 2;
294   }
295   d = x & 255;
296   c = (x >> 8) & 255;
297   b = (x >> 16) & 255;
298   a = (x >> 24) & 255;
299   sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
300   if (a == 255)
301     sw -= alpha;
302   else
303     assert(a == 0);
304   return sw;
305 }
306
307 void dvi_printer::set_color(color *col)
308 {
309   cur_color = *col;
310   char buf[256];
311   unsigned int components[4];
312   color_scheme cs = col->get_components(components);
313   switch (cs) {
314   case DEFAULT:
315     sprintf(buf, "color gray 0");
316     break;
317   case RGB:
318     sprintf(buf, "color rgb %.3g %.3g %.3g",
319                  double(Red) / color::MAX_COLOR_VAL,
320                  double(Green) / color::MAX_COLOR_VAL,
321                  double(Blue) / color::MAX_COLOR_VAL);
322     break;
323   case CMY:
324     col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
325     // fall through
326   case CMYK:
327     sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
328                  double(Cyan) / color::MAX_COLOR_VAL,
329                  double(Magenta) / color::MAX_COLOR_VAL,
330                  double(Yellow) / color::MAX_COLOR_VAL,
331                  double(Black) / color::MAX_COLOR_VAL);
332     break;
333   case GRAY:
334     sprintf(buf, "color gray %.3g",
335                  double(Gray) / color::MAX_COLOR_VAL);
336     break;
337   }
338   do_special(buf);
339 }
340
341 void dvi_printer::set_char(glyph *g, font *f, const environment *env,
342                            int w, const char *)
343 {
344   if (*env->col != cur_color)
345     set_color(env->col);
346   int code = f->get_code(g);
347   if (env->size != cur_point_size || f != cur_font) {
348     cur_font = f;
349     cur_point_size = env->size;
350     int i;
351     for (i = 0;; i++) {
352       if (i >= FONTS_MAX) {
353         fatal("too many output fonts required");
354       }
355       if (output_font_table[i].f == 0) {
356         output_font_table[i].f = (dvi_font *)cur_font;
357         output_font_table[i].point_size = cur_point_size;
358         define_font(i);
359       }
360       if (output_font_table[i].f == cur_font
361           && output_font_table[i].point_size == cur_point_size)
362         break;
363     }
364     set_font(i);
365   }
366   int distance = env->hpos - cur_h;
367   if (env->hpos != end_h && distance != 0) {
368     out_signed(right1, distance);
369     cur_h = env->hpos;
370   }
371   else if (distance > max_drift) {
372     out_signed(right1, distance - max_drift);
373     cur_h = env->hpos - max_drift;
374   }
375   else if (distance < -max_drift) {
376     out_signed(right1, distance + max_drift);
377     cur_h = env->hpos + max_drift;
378   }
379   if (env->vpos != cur_v) {
380     out_signed(down1, env->vpos - cur_v);
381     cur_v = env->vpos;
382   }
383   possibly_begin_line();
384   end_h = env->hpos + w;
385   cur_h += scale(f->get_width(g, UNITWIDTH) / MULTIPLIER,
386                  cur_point_size * RES_7227);
387   if (cur_h > max_h)
388     max_h = cur_h;
389   if (cur_v > max_v)
390     max_v = cur_v;
391   if (code >= 0 && code <= 127)
392     out1(code);
393   else
394     out_unsigned(set1, code);
395 }
396
397 void dvi_printer::define_font(int i)
398 {
399   out_unsigned(fnt_def1, i);
400   dvi_font *f = output_font_table[i].f;
401   out4(f->checksum);
402   out4(output_font_table[i].point_size*RES_7227);
403   out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
404   const char *nm = f->get_internal_name();
405   out1(0);
406   out_string(nm);
407 }
408
409 void dvi_printer::set_font(int i)
410 {
411   if (i >= 0 && i <= 63)
412     out1(fnt_num_0 + i);
413   else
414     out_unsigned(fnt1, i);
415 }
416
417 void dvi_printer::out_signed(unsigned char base, int param)
418 {
419   if (-128 <= param && param < 128) {
420     out1(base);
421     out1(param);
422   }
423   else if (-32768 <= param && param < 32768) {
424     out1(base+1);
425     out2(param);
426   }
427   else if (-(1 << 23) <= param && param < (1 << 23)) {
428     out1(base+2);
429     out3(param);
430   }
431   else {
432     out1(base+3);
433     out4(param);
434   }
435 }
436
437 void dvi_printer::out_unsigned(unsigned char base, int param)
438 {
439   if (param >= 0) {
440     if (param < 256) {
441       out1(base);
442       out1(param);
443     }
444     else if (param < 65536) {
445       out1(base+1);
446       out2(param);
447     }
448     else if (param < (1 << 24)) {
449       out1(base+2);
450       out3(param);
451     }
452     else {
453       out1(base+3);
454       out4(param);
455     }
456   }
457   else {
458     out1(base+3);
459     out4(param);
460   }
461 }
462
463 void dvi_printer::preamble()
464 {
465   out1(pre);
466   out1(id_byte);
467   out4(254000);
468   out4(font::res);
469   out4(1000);
470   out1(0);
471 }
472
473 void dvi_printer::postamble()
474 {
475   int tem = byte_count;
476   out1(post);
477   out4(last_bop);
478   out4(254000);
479   out4(font::res);
480   out4(1000);
481   out4(max_v);
482   out4(max_h);
483   out2(have_pushed); // stack depth
484   out2(page_count);
485   int i;
486   for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
487     define_font(i);
488   out1(post_post);
489   out4(tem);
490   out1(id_byte);
491   for (i = 0; i < 4 || byte_count % 4 != 0; i++)
492     out1(filler);
493 }  
494   
495 void dvi_printer::begin_page(int i)
496 {
497   page_count++;
498   int tem = byte_count;
499   out1(bop);
500   out4(i);
501   for (int j = 1; j < 10; j++)
502     out4(0);
503   out4(last_bop);
504   last_bop = tem;
505   // By convention position (0,0) in a dvi file is placed at (1in, 1in).
506   cur_h = font::res;
507   cur_v = font::res;
508   end_h = 0;
509   if (page_count == 1) {
510     char buf[256];
511     // at least dvips uses this
512     double length = user_paper_length ? user_paper_length :
513                                         double(font::paperlength) / font::res;
514     double width = user_paper_width ? user_paper_width :
515                                       double(font::paperwidth) / font::res;
516     if (width > 0 && length > 0) {
517       sprintf(buf, "papersize=%.3fin,%.3fin",
518               landscape_flag ? length : width,
519               landscape_flag ? width : length);
520       do_special(buf);
521     }
522   }
523   if (cur_color != default_color)
524     set_color(&cur_color);
525 }
526
527 void dvi_printer::end_page(int)
528 {
529   set_color(&default_color);
530   if (pushed)
531     end_of_line();
532   out1(eop);
533   cur_font = 0;
534 }
535
536 void draw_dvi_printer::end_page(int len)
537 {
538   dvi_printer::end_page(len);
539   output_pen_size = -1;
540 }
541
542 void dvi_printer::do_special(const char *s)
543 {
544   int len = strlen(s);
545   if (len == 0)
546     return;
547   possibly_begin_line();
548   out_unsigned(xxx1, len);
549   while (*s)
550     out1(*s++);
551 }
552
553 void dvi_printer::special(char *arg, const environment *env, char type)
554 {
555   if (type != 'p')
556     return;
557   moveto(env->hpos, env->vpos);
558   do_special(arg);
559 }
560
561 void dvi_printer::moveto(int h, int v)
562 {
563   if (h != cur_h) {
564     out_signed(right1, h - cur_h);
565     cur_h = h;
566     if (cur_h > max_h)
567       max_h = cur_h;
568   }
569   if (v != cur_v) {
570     out_signed(down1, v - cur_v);
571     cur_v = v;
572     if (cur_v > max_v)
573       max_v = cur_v;
574   }
575   end_h = 0;
576 }
577
578 void dvi_printer::draw(int code, int *p, int np, const environment *env)
579 {
580   if (code == 'l') {
581     int x = 0, y = 0;
582     int height = 0, width = 0;
583     int thickness;
584     if (line_thickness < 0)
585       thickness = env->size*RES_7227*linewidth/1000;
586     else if (line_thickness > 0)
587       thickness = line_thickness;
588     else
589       thickness = 1;
590     if (np != 2) {
591       error("2 arguments required for line");
592     }
593     else if (p[0] == 0) {
594       // vertical rule
595       if (p[1] > 0) {
596         x = env->hpos - thickness/2;
597         y = env->vpos + p[1] + thickness/2;
598         height = p[1] + thickness;
599         width = thickness;
600       }
601       else if (p[1] < 0) {
602         x = env->hpos - thickness/2;
603         y = env->vpos + thickness/2;
604         height = thickness - p[1];
605         width = thickness;
606       }
607     }
608     else if (p[1] == 0) {
609       if (p[0] > 0) {
610         x = env->hpos - thickness/2;
611         y = env->vpos + thickness/2;
612         height = thickness;
613         width = p[0] + thickness;
614       }
615       else if (p[0] < 0) {
616         x = env->hpos - p[0] - thickness/2;
617         y = env->vpos + thickness/2;
618         height = thickness;
619         width = thickness - p[0];
620       }
621     }
622     if (height != 0) {
623       moveto(x, y);
624       out1(put_rule);
625       out4(height);
626       out4(width);
627     }
628   }
629   else if (code == 't') {
630     if (np == 0) {
631       line_thickness = -1;
632     }
633     else {
634       // troff gratuitously adds an extra 0
635       if (np != 1 && np != 2)
636         error("0 or 1 argument required for thickness");
637       else
638         line_thickness = p[0];
639     }
640   }
641   else if (code == 'R') {
642     if (np != 2)
643       error("2 arguments required for rule");
644     else if (p[0] != 0 || p[1] != 0) {
645       int dh = p[0];
646       int dv = p[1];
647       int oh = env->hpos;
648       int ov = env->vpos;
649       if (dv > 0) {
650         ov += dv;
651         dv = -dv;
652       }
653       if (dh < 0) {
654         oh += dh;
655         dh = -dh;
656       }
657       moveto(oh, ov);
658       out1(put_rule);
659       out4(-dv);
660       out4(dh);
661     }
662   }
663 }
664
665 // XXX Will this overflow?
666
667 inline int milliinches(int n)
668 {
669   return (n*1000 + font::res/2)/font::res;
670 }
671
672 void draw_dvi_printer::set_line_thickness(const environment *env)
673 {
674   int desired_pen_size
675     = milliinches(line_thickness < 0
676                   // Will this overflow?
677                   ? env->size*RES_7227*linewidth/1000
678                   : line_thickness);
679   if (desired_pen_size != output_pen_size) {
680     char buf[256];
681     sprintf(buf, "pn %d", desired_pen_size);
682     do_special(buf);
683     output_pen_size = desired_pen_size;
684   }
685 }
686
687 void draw_dvi_printer::fill_next(const environment *env)
688 {
689   unsigned int g;
690   if (env->fill->is_default())
691     g = 0;
692   else {
693     // currently, only BW support
694     env->fill->get_gray(&g);
695   }
696   char buf[256];
697   sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
698   do_special(buf);
699 }
700
701 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
702 {
703   char buf[1024];
704   int fill_flag = 0;
705   switch (code) {
706   case 'C':
707     fill_flag = 1;
708     // fall through
709   case 'c':
710     {
711       // troff adds an extra argument to C
712       if (np != 1 && !(code == 'C' && np == 2)) {
713         error("1 argument required for circle");
714         break;
715       }
716       moveto(env->hpos+p[0]/2, env->vpos);
717       if (fill_flag)
718         fill_next(env);
719       else
720         set_line_thickness(env);
721       int rad;
722       rad = milliinches(p[0]/2);
723       sprintf(buf, "%s 0 0 %d %d 0 6.28319",
724               (fill_flag ? "ia" : "ar"),
725               rad,
726               rad);
727       do_special(buf);
728       break;
729     }
730   case 'l':
731     if (np != 2) {
732       error("2 arguments required for line");
733       break;
734     }
735     moveto(env->hpos, env->vpos);
736     set_line_thickness(env);
737     do_special("pa 0 0");
738     sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
739     do_special(buf);
740     do_special("fp");
741     break;
742   case 'E':
743     fill_flag = 1;
744     // fall through
745   case 'e':
746     if (np != 2) {
747       error("2 arguments required for ellipse");
748       break;
749     }
750     moveto(env->hpos+p[0]/2, env->vpos);
751     if (fill_flag)
752       fill_next(env);
753     else
754       set_line_thickness(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 }