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
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22#include "driver.h"
23#include "nonposix.h"
24#include "paper.h"
25
26extern "C" const char *Version_string;
27
28#define DEFAULT_LINEWIDTH 40
29static int linewidth = DEFAULT_LINEWIDTH;
30
31static int draw_flag = 1;
32
33static int landscape_flag = 0;
34static double user_paper_length = 0;
35static double user_paper_width = 0;
36
37/* These values were chosen because:
38
39(MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
40
41and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
42
43The width in the groff font file is the product of MULTIPLIER and the
44width 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
52class dvi_font : public font {
53  dvi_font(const char *);
54public:
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
63dvi_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
73dvi_font::dvi_font(const char *nm)
74: font(nm), checksum(0), design_size(0)
75{
76}
77
78dvi_font::~dvi_font()
79{
80}
81
82void 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
109struct output_font {
110  dvi_font *f;
111  int point_size;
112  output_font() : f(0) { }
113};
114
115class 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 *);
140protected:
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 *);
172public:
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
185class draw_dvi_printer : public dvi_printer {
186  int output_pen_size;
187  void set_line_thickness(const environment *);
188  void fill_next(const environment *);
189public:
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
196dvi_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
214dvi_printer::~dvi_printer()
215{
216  postamble();
217}
218
219
220draw_dvi_printer::draw_dvi_printer()
221: output_pen_size(-1)
222{
223}
224
225draw_dvi_printer::~draw_dvi_printer()
226{
227}
228
229
230void dvi_printer::out1(int n)
231{
232  byte_count += 1;
233  putc(n & 0xff, fp);
234}
235
236void dvi_printer::out2(int n)
237{
238  byte_count += 2;
239  putc((n >> 8) & 0xff, fp);
240  putc(n & 0xff, fp);
241}
242
243void 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
251void 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
260void dvi_printer::out_string(const char *s)
261{
262  out1(strlen(s));
263  while (*s != 0)
264    out1(*s++);
265}
266
267
268void 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
278void 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
288int 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
309void 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
343void 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
399void 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
411void 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
419void 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
439void 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
465void 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
475void 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
497void 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
529void 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
538void draw_dvi_printer::end_page(int len)
539{
540  dvi_printer::end_page(len);
541  output_pen_size = -1;
542}
543
544void 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
555void 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
563void 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
580void 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
669inline int milliinches(int n)
670{
671  return (n*1000 + font::res/2)/font::res;
672}
673
674void 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
689void 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
703void 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
893font *dvi_printer::make_font(const char *nm)
894{
895  return dvi_font::load_dvi_font(nm);
896}
897
898printer *make_printer()
899{
900  if (draw_flag)
901    return new draw_dvi_printer;
902  else
903    return new dvi_printer;
904}
905
906static void usage(FILE *stream);
907
908int 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
974static void usage(FILE *stream)
975{
976  fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
977	  program_name);
978}
979