1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "driver.h"
23114402Sru#include "nonposix.h"
24114402Sru#include "paper.h"
25114402Sru
26114402Sruextern "C" const char *Version_string;
27114402Sru
28114402Sru#define DEFAULT_LINEWIDTH 40
29114402Srustatic int linewidth = DEFAULT_LINEWIDTH;
30114402Sru
31114402Srustatic int draw_flag = 1;
32114402Sru
33114402Srustatic int landscape_flag = 0;
34114402Srustatic double user_paper_length = 0;
35114402Srustatic double user_paper_width = 0;
36114402Sru
37114402Sru/* These values were chosen because:
38114402Sru
39114402Sru(MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
40114402Sru
41114402Sruand 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
42114402Sru
43114402SruThe width in the groff font file is the product of MULTIPLIER and the
44114402Sruwidth in the tfm file. */
45114402Sru
46114402Sru#define RES 57816
47114402Sru#define RES_7227 (RES/7227)
48114402Sru#define UNITWIDTH 131072
49114402Sru#define SIZESCALE 100
50114402Sru#define MULTIPLIER 1
51114402Sru
52114402Sruclass dvi_font : public font {
53114402Sru  dvi_font(const char *);
54114402Srupublic:
55114402Sru  int checksum;
56114402Sru  int design_size;
57114402Sru  ~dvi_font();
58114402Sru  void handle_unknown_font_command(const char *command, const char *arg,
59114402Sru				   const char *filename, int lineno);
60114402Sru  static dvi_font *load_dvi_font(const char *);
61114402Sru};
62114402Sru
63114402Srudvi_font *dvi_font::load_dvi_font(const char *s)
64114402Sru{
65114402Sru  dvi_font *f = new dvi_font(s);
66114402Sru  if (!f->load()) {
67114402Sru    delete f;
68114402Sru    return 0;
69114402Sru  }
70114402Sru  return f;
71114402Sru}
72114402Sru
73114402Srudvi_font::dvi_font(const char *nm)
74114402Sru: font(nm), checksum(0), design_size(0)
75114402Sru{
76114402Sru}
77114402Sru
78114402Srudvi_font::~dvi_font()
79114402Sru{
80114402Sru}
81114402Sru
82114402Sruvoid dvi_font::handle_unknown_font_command(const char *command,
83114402Sru					   const char *arg,
84114402Sru					   const char *filename, int lineno)
85114402Sru{
86114402Sru  char *ptr;
87114402Sru  if (strcmp(command, "checksum") == 0) {
88114402Sru    if (arg == 0)
89114402Sru      fatal_with_file_and_line(filename, lineno,
90114402Sru			       "`checksum' command requires an argument");
91114402Sru    checksum = int(strtol(arg, &ptr, 10));
92114402Sru    if (checksum == 0 && ptr == arg) {
93114402Sru      fatal_with_file_and_line(filename, lineno, "bad checksum");
94114402Sru    }
95114402Sru  }
96114402Sru  else if (strcmp(command, "designsize") == 0) {
97114402Sru    if (arg == 0)
98114402Sru      fatal_with_file_and_line(filename, lineno,
99114402Sru			       "`designsize' command requires an argument");
100114402Sru    design_size = int(strtol(arg, &ptr, 10));
101114402Sru    if (design_size == 0 && ptr == arg) {
102114402Sru      fatal_with_file_and_line(filename, lineno, "bad design size");
103114402Sru    }
104114402Sru  }
105114402Sru}
106114402Sru
107114402Sru#define FONTS_MAX 256
108114402Sru
109114402Srustruct output_font {
110114402Sru  dvi_font *f;
111114402Sru  int point_size;
112114402Sru  output_font() : f(0) { }
113114402Sru};
114114402Sru
115114402Sruclass dvi_printer : public printer {
116114402Sru  FILE *fp;
117114402Sru  int max_drift;
118114402Sru  int byte_count;
119114402Sru  int last_bop;
120114402Sru  int page_count;
121114402Sru  int cur_h;
122114402Sru  int cur_v;
123114402Sru  int end_h;
124114402Sru  int max_h;
125114402Sru  int max_v;
126114402Sru  output_font output_font_table[FONTS_MAX];
127114402Sru  font *cur_font;
128114402Sru  int cur_point_size;
129114402Sru  color cur_color;
130114402Sru  int pushed;
131114402Sru  int pushed_h;
132114402Sru  int pushed_v;
133114402Sru  int have_pushed;
134114402Sru  void preamble();
135114402Sru  void postamble();
136114402Sru  void define_font(int);
137114402Sru  void set_font(int);
138114402Sru  void possibly_begin_line();
139114402Sru  void set_color(color *);
140114402Sruprotected:
141114402Sru  enum {
142114402Sru    id_byte = 2,
143114402Sru    set1 = 128,
144114402Sru    put1 = 133,
145114402Sru    put_rule = 137,
146114402Sru    bop = 139,
147114402Sru    eop = 140,
148114402Sru    push = 141,
149114402Sru    pop = 142,
150114402Sru    right1 = 143,
151114402Sru    down1 = 157,
152114402Sru    fnt_num_0 = 171,
153114402Sru    fnt1 = 235,
154114402Sru    xxx1 = 239,
155114402Sru    fnt_def1 = 243,
156114402Sru    pre = 247,
157114402Sru    post = 248,
158114402Sru    post_post = 249,
159114402Sru    filler = 223
160114402Sru  };
161114402Sru  int line_thickness;
162114402Sru
163114402Sru  void out1(int);
164114402Sru  void out2(int);
165114402Sru  void out3(int);
166114402Sru  void out4(int);
167114402Sru  void moveto(int, int);
168114402Sru  void out_string(const char *);
169114402Sru  void out_signed(unsigned char, int);
170114402Sru  void out_unsigned(unsigned char, int);
171114402Sru  void do_special(const char *);
172114402Srupublic:
173114402Sru  dvi_printer();
174114402Sru  ~dvi_printer();
175114402Sru  font *make_font(const char *);
176114402Sru  void begin_page(int);
177114402Sru  void end_page(int);
178114402Sru  void set_char(int, font *, const environment *, int w, const char *name);
179114402Sru  void special(char *arg, const environment *env, char type);
180114402Sru  void end_of_line();
181114402Sru  void draw(int code, int *p, int np, const environment *env);
182114402Sru};
183114402Sru
184114402Sru
185114402Sruclass draw_dvi_printer : public dvi_printer {
186114402Sru  int output_pen_size;
187114402Sru  void set_line_thickness(const environment *);
188114402Sru  void fill_next(const environment *);
189114402Srupublic:
190114402Sru  draw_dvi_printer();
191114402Sru  ~draw_dvi_printer();
192114402Sru  void draw(int code, int *p, int np, const environment *env);
193114402Sru  void end_page(int);
194114402Sru};
195114402Sru
196114402Srudvi_printer::dvi_printer()
197114402Sru: fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
198114402Sru  cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
199114402Sru{
200114402Sru  if (font::res != RES)
201114402Sru    fatal("resolution must be %1", RES);
202114402Sru  if (font::unitwidth != UNITWIDTH)
203114402Sru    fatal("unitwidth must be %1", UNITWIDTH);
204114402Sru  if (font::hor != 1)
205114402Sru    fatal("hor must be equal to 1");
206114402Sru  if (font::vert != 1)
207114402Sru    fatal("vert must be equal to 1");
208114402Sru  if (font::sizescale != SIZESCALE)
209114402Sru    fatal("sizescale must be equal to %1", SIZESCALE);
210114402Sru  max_drift = font::res/1000;	// this is fairly arbitrary
211114402Sru  preamble();
212114402Sru}
213114402Sru
214114402Srudvi_printer::~dvi_printer()
215114402Sru{
216114402Sru  postamble();
217114402Sru}
218114402Sru
219114402Sru
220114402Srudraw_dvi_printer::draw_dvi_printer()
221114402Sru: output_pen_size(-1)
222114402Sru{
223114402Sru}
224114402Sru
225114402Srudraw_dvi_printer::~draw_dvi_printer()
226114402Sru{
227114402Sru}
228114402Sru
229114402Sru
230114402Sruvoid dvi_printer::out1(int n)
231114402Sru{
232114402Sru  byte_count += 1;
233114402Sru  putc(n & 0xff, fp);
234114402Sru}
235114402Sru
236114402Sruvoid dvi_printer::out2(int n)
237114402Sru{
238114402Sru  byte_count += 2;
239114402Sru  putc((n >> 8) & 0xff, fp);
240114402Sru  putc(n & 0xff, fp);
241114402Sru}
242114402Sru
243114402Sruvoid dvi_printer::out3(int n)
244114402Sru{
245114402Sru  byte_count += 3;
246114402Sru  putc((n >> 16) & 0xff, fp);
247114402Sru  putc((n >> 8) & 0xff, fp);
248114402Sru  putc(n & 0xff, fp);
249114402Sru}
250114402Sru
251114402Sruvoid dvi_printer::out4(int n)
252114402Sru{
253114402Sru  byte_count += 4;
254114402Sru  putc((n >> 24) & 0xff, fp);
255114402Sru  putc((n >> 16) & 0xff, fp);
256114402Sru  putc((n >> 8) & 0xff, fp);
257114402Sru  putc(n & 0xff, fp);
258114402Sru}
259114402Sru
260114402Sruvoid dvi_printer::out_string(const char *s)
261114402Sru{
262114402Sru  out1(strlen(s));
263114402Sru  while (*s != 0)
264114402Sru    out1(*s++);
265114402Sru}
266114402Sru
267114402Sru
268114402Sruvoid dvi_printer::end_of_line()
269114402Sru{
270114402Sru  if (pushed) {
271114402Sru    out1(pop);
272114402Sru    pushed = 0;
273114402Sru    cur_h = pushed_h;
274114402Sru    cur_v = pushed_v;
275114402Sru  }
276114402Sru}
277114402Sru
278114402Sruvoid dvi_printer::possibly_begin_line()
279114402Sru{
280114402Sru  if (!pushed) {
281114402Sru    have_pushed = pushed = 1;
282114402Sru    pushed_h = cur_h;
283114402Sru    pushed_v = cur_v;
284114402Sru    out1(push);
285114402Sru  }
286114402Sru}
287114402Sru
288114402Sruint scale(int x, int z)
289114402Sru{
290114402Sru  int sw;
291114402Sru  int a, b, c, d;
292114402Sru  int alpha, beta;
293114402Sru  alpha = 16*z; beta = 16;
294114402Sru  while (z >= 040000000L) {
295114402Sru    z /= 2; beta /= 2;
296114402Sru  }
297114402Sru  d = x & 255;
298114402Sru  c = (x >> 8) & 255;
299114402Sru  b = (x >> 16) & 255;
300114402Sru  a = (x >> 24) & 255;
301114402Sru  sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
302114402Sru  if (a == 255)
303114402Sru    sw -= alpha;
304114402Sru  else
305114402Sru    assert(a == 0);
306114402Sru  return sw;
307114402Sru}
308114402Sru
309114402Sruvoid dvi_printer::set_color(color *col)
310114402Sru{
311114402Sru  cur_color = *col;
312114402Sru  char buf[256];
313114402Sru  unsigned int components[4];
314114402Sru  color_scheme cs = col->get_components(components);
315114402Sru  switch (cs) {
316114402Sru  case DEFAULT:
317114402Sru    sprintf(buf, "color gray 0");
318114402Sru    break;
319114402Sru  case RGB:
320114402Sru    sprintf(buf, "color rgb %.3g %.3g %.3g",
321114402Sru		 double(Red) / color::MAX_COLOR_VAL,
322114402Sru		 double(Green) / color::MAX_COLOR_VAL,
323114402Sru		 double(Blue) / color::MAX_COLOR_VAL);
324114402Sru    break;
325114402Sru  case CMY:
326114402Sru    col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
327114402Sru    // fall through
328114402Sru  case CMYK:
329114402Sru    sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
330114402Sru		 double(Cyan) / color::MAX_COLOR_VAL,
331114402Sru		 double(Magenta) / color::MAX_COLOR_VAL,
332114402Sru		 double(Yellow) / color::MAX_COLOR_VAL,
333114402Sru		 double(Black) / color::MAX_COLOR_VAL);
334114402Sru    break;
335114402Sru  case GRAY:
336114402Sru    sprintf(buf, "color gray %.3g",
337114402Sru		 double(Gray) / color::MAX_COLOR_VAL);
338114402Sru    break;
339114402Sru  }
340114402Sru  do_special(buf);
341114402Sru}
342114402Sru
343151497Sruvoid dvi_printer::set_char(int idx, font *f, const environment *env,
344114402Sru			   int w, const char *)
345114402Sru{
346114402Sru  if (*env->col != cur_color)
347114402Sru    set_color(env->col);
348151497Sru  int code = f->get_code(idx);
349114402Sru  if (env->size != cur_point_size || f != cur_font) {
350114402Sru    cur_font = f;
351114402Sru    cur_point_size = env->size;
352114402Sru    int i;
353114402Sru    for (i = 0;; i++) {
354114402Sru      if (i >= FONTS_MAX) {
355114402Sru	fatal("too many output fonts required");
356114402Sru      }
357114402Sru      if (output_font_table[i].f == 0) {
358114402Sru	output_font_table[i].f = (dvi_font *)cur_font;
359114402Sru	output_font_table[i].point_size = cur_point_size;
360114402Sru	define_font(i);
361114402Sru      }
362114402Sru      if (output_font_table[i].f == cur_font
363114402Sru	  && output_font_table[i].point_size == cur_point_size)
364114402Sru	break;
365114402Sru    }
366114402Sru    set_font(i);
367114402Sru  }
368114402Sru  int distance = env->hpos - cur_h;
369114402Sru  if (env->hpos != end_h && distance != 0) {
370114402Sru    out_signed(right1, distance);
371114402Sru    cur_h = env->hpos;
372114402Sru  }
373114402Sru  else if (distance > max_drift) {
374114402Sru    out_signed(right1, distance - max_drift);
375114402Sru    cur_h = env->hpos - max_drift;
376114402Sru  }
377114402Sru  else if (distance < -max_drift) {
378114402Sru    out_signed(right1, distance + max_drift);
379114402Sru    cur_h = env->hpos + max_drift;
380114402Sru  }
381114402Sru  if (env->vpos != cur_v) {
382114402Sru    out_signed(down1, env->vpos - cur_v);
383114402Sru    cur_v = env->vpos;
384114402Sru  }
385114402Sru  possibly_begin_line();
386114402Sru  end_h = env->hpos + w;
387151497Sru  cur_h += scale(f->get_width(idx, UNITWIDTH)/MULTIPLIER,
388114402Sru		 cur_point_size*RES_7227);
389114402Sru  if (cur_h > max_h)
390114402Sru    max_h = cur_h;
391114402Sru  if (cur_v > max_v)
392114402Sru    max_v = cur_v;
393114402Sru  if (code >= 0 && code <= 127)
394114402Sru    out1(code);
395114402Sru  else
396114402Sru    out_unsigned(set1, code);
397114402Sru}
398114402Sru
399114402Sruvoid dvi_printer::define_font(int i)
400114402Sru{
401114402Sru  out_unsigned(fnt_def1, i);
402114402Sru  dvi_font *f = output_font_table[i].f;
403114402Sru  out4(f->checksum);
404114402Sru  out4(output_font_table[i].point_size*RES_7227);
405114402Sru  out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
406114402Sru  const char *nm = f->get_internal_name();
407114402Sru  out1(0);
408114402Sru  out_string(nm);
409114402Sru}
410114402Sru
411114402Sruvoid dvi_printer::set_font(int i)
412114402Sru{
413114402Sru  if (i >= 0 && i <= 63)
414114402Sru    out1(fnt_num_0 + i);
415114402Sru  else
416114402Sru    out_unsigned(fnt1, i);
417114402Sru}
418114402Sru
419114402Sruvoid dvi_printer::out_signed(unsigned char base, int param)
420114402Sru{
421114402Sru  if (-128 <= param && param < 128) {
422114402Sru    out1(base);
423114402Sru    out1(param);
424114402Sru  }
425114402Sru  else if (-32768 <= param && param < 32768) {
426114402Sru    out1(base+1);
427114402Sru    out2(param);
428114402Sru  }
429114402Sru  else if (-(1 << 23) <= param && param < (1 << 23)) {
430114402Sru    out1(base+2);
431114402Sru    out3(param);
432114402Sru  }
433114402Sru  else {
434114402Sru    out1(base+3);
435114402Sru    out4(param);
436114402Sru  }
437114402Sru}
438114402Sru
439114402Sruvoid dvi_printer::out_unsigned(unsigned char base, int param)
440114402Sru{
441114402Sru  if (param >= 0) {
442114402Sru    if (param < 256) {
443114402Sru      out1(base);
444114402Sru      out1(param);
445114402Sru    }
446114402Sru    else if (param < 65536) {
447114402Sru      out1(base+1);
448114402Sru      out2(param);
449114402Sru    }
450114402Sru    else if (param < (1 << 24)) {
451114402Sru      out1(base+2);
452114402Sru      out3(param);
453114402Sru    }
454114402Sru    else {
455114402Sru      out1(base+3);
456114402Sru      out4(param);
457114402Sru    }
458114402Sru  }
459114402Sru  else {
460114402Sru    out1(base+3);
461114402Sru    out4(param);
462114402Sru  }
463114402Sru}
464114402Sru
465114402Sruvoid dvi_printer::preamble()
466114402Sru{
467114402Sru  out1(pre);
468114402Sru  out1(id_byte);
469114402Sru  out4(254000);
470114402Sru  out4(font::res);
471114402Sru  out4(1000);
472114402Sru  out1(0);
473114402Sru}
474114402Sru
475114402Sruvoid dvi_printer::postamble()
476114402Sru{
477114402Sru  int tem = byte_count;
478114402Sru  out1(post);
479114402Sru  out4(last_bop);
480114402Sru  out4(254000);
481114402Sru  out4(font::res);
482114402Sru  out4(1000);
483114402Sru  out4(max_v);
484114402Sru  out4(max_h);
485114402Sru  out2(have_pushed); // stack depth
486114402Sru  out2(page_count);
487114402Sru  int i;
488114402Sru  for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
489114402Sru    define_font(i);
490114402Sru  out1(post_post);
491114402Sru  out4(tem);
492114402Sru  out1(id_byte);
493114402Sru  for (i = 0; i < 4 || byte_count % 4 != 0; i++)
494114402Sru    out1(filler);
495114402Sru}
496114402Sru
497114402Sruvoid dvi_printer::begin_page(int i)
498114402Sru{
499114402Sru  page_count++;
500114402Sru  int tem = byte_count;
501114402Sru  out1(bop);
502114402Sru  out4(i);
503114402Sru  for (int j = 1; j < 10; j++)
504114402Sru    out4(0);
505114402Sru  out4(last_bop);
506114402Sru  last_bop = tem;
507114402Sru  // By convention position (0,0) in a dvi file is placed at (1in, 1in).
508114402Sru  cur_h = font::res;
509114402Sru  cur_v = font::res;
510114402Sru  end_h = 0;
511114402Sru  if (page_count == 1) {
512114402Sru    char buf[256];
513114402Sru    // at least dvips uses this
514114402Sru    double length = user_paper_length ? user_paper_length :
515114402Sru					double(font::paperlength) / font::res;
516114402Sru    double width = user_paper_width ? user_paper_width :
517114402Sru				      double(font::paperwidth) / font::res;
518114402Sru    if (width > 0 && length > 0) {
519114402Sru      sprintf(buf, "papersize=%.3fin,%.3fin",
520114402Sru	      landscape_flag ? length : width,
521114402Sru	      landscape_flag ? width : length);
522114402Sru      do_special(buf);
523114402Sru    }
524114402Sru  }
525114402Sru  if (cur_color != default_color)
526114402Sru    set_color(&cur_color);
527114402Sru}
528114402Sru
529114402Sruvoid dvi_printer::end_page(int)
530114402Sru{
531114402Sru  set_color(&default_color);
532114402Sru  if (pushed)
533114402Sru    end_of_line();
534114402Sru  out1(eop);
535114402Sru  cur_font = 0;
536114402Sru}
537114402Sru
538114402Sruvoid draw_dvi_printer::end_page(int len)
539114402Sru{
540114402Sru  dvi_printer::end_page(len);
541114402Sru  output_pen_size = -1;
542114402Sru}
543114402Sru
544114402Sruvoid dvi_printer::do_special(const char *s)
545114402Sru{
546114402Sru  int len = strlen(s);
547114402Sru  if (len == 0)
548114402Sru    return;
549114402Sru  possibly_begin_line();
550114402Sru  out_unsigned(xxx1, len);
551114402Sru  while (*s)
552114402Sru    out1(*s++);
553114402Sru}
554114402Sru
555114402Sruvoid dvi_printer::special(char *arg, const environment *env, char type)
556114402Sru{
557114402Sru  if (type != 'p')
558114402Sru    return;
559114402Sru  moveto(env->hpos, env->vpos);
560114402Sru  do_special(arg);
561114402Sru}
562114402Sru
563114402Sruvoid dvi_printer::moveto(int h, int v)
564114402Sru{
565114402Sru  if (h != cur_h) {
566114402Sru    out_signed(right1, h - cur_h);
567114402Sru    cur_h = h;
568114402Sru    if (cur_h > max_h)
569114402Sru      max_h = cur_h;
570114402Sru  }
571114402Sru  if (v != cur_v) {
572114402Sru    out_signed(down1, v - cur_v);
573114402Sru    cur_v = v;
574114402Sru    if (cur_v > max_v)
575114402Sru      max_v = cur_v;
576114402Sru  }
577114402Sru  end_h = 0;
578114402Sru}
579114402Sru
580114402Sruvoid dvi_printer::draw(int code, int *p, int np, const environment *env)
581114402Sru{
582114402Sru  if (code == 'l') {
583114402Sru    int x = 0, y = 0;
584114402Sru    int height = 0, width = 0;
585114402Sru    int thickness;
586114402Sru    if (line_thickness < 0)
587114402Sru      thickness = env->size*RES_7227*linewidth/1000;
588114402Sru    else if (line_thickness > 0)
589114402Sru      thickness = line_thickness;
590114402Sru    else
591114402Sru      thickness = 1;
592114402Sru    if (np != 2) {
593114402Sru      error("2 arguments required for line");
594114402Sru    }
595114402Sru    else if (p[0] == 0) {
596114402Sru      // vertical rule
597114402Sru      if (p[1] > 0) {
598114402Sru	x = env->hpos - thickness/2;
599114402Sru	y = env->vpos + p[1] + thickness/2;
600114402Sru	height = p[1] + thickness;
601114402Sru	width = thickness;
602114402Sru      }
603114402Sru      else if (p[1] < 0) {
604114402Sru	x = env->hpos - thickness/2;
605114402Sru	y = env->vpos + thickness/2;
606114402Sru	height = thickness - p[1];
607114402Sru	width = thickness;
608114402Sru      }
609114402Sru    }
610114402Sru    else if (p[1] == 0) {
611114402Sru      if (p[0] > 0) {
612114402Sru	x = env->hpos - thickness/2;
613114402Sru	y = env->vpos + thickness/2;
614114402Sru	height = thickness;
615114402Sru	width = p[0] + thickness;
616114402Sru      }
617114402Sru      else if (p[0] < 0) {
618114402Sru	x = env->hpos - p[0] - thickness/2;
619114402Sru	y = env->vpos + thickness/2;
620114402Sru	height = thickness;
621114402Sru	width = thickness - p[0];
622114402Sru      }
623114402Sru    }
624114402Sru    if (height != 0) {
625114402Sru      moveto(x, y);
626114402Sru      out1(put_rule);
627114402Sru      out4(height);
628114402Sru      out4(width);
629114402Sru    }
630114402Sru  }
631114402Sru  else if (code == 't') {
632114402Sru    if (np == 0) {
633114402Sru      line_thickness = -1;
634114402Sru    }
635114402Sru    else {
636114402Sru      // troff gratuitously adds an extra 0
637114402Sru      if (np != 1 && np != 2)
638114402Sru	error("0 or 1 argument required for thickness");
639114402Sru      else
640114402Sru	line_thickness = p[0];
641114402Sru    }
642114402Sru  }
643114402Sru  else if (code == 'R') {
644114402Sru    if (np != 2)
645114402Sru      error("2 arguments required for rule");
646114402Sru    else if (p[0] != 0 || p[1] != 0) {
647114402Sru      int dh = p[0];
648114402Sru      int dv = p[1];
649114402Sru      int oh = env->hpos;
650114402Sru      int ov = env->vpos;
651114402Sru      if (dv > 0) {
652114402Sru	ov += dv;
653114402Sru	dv = -dv;
654114402Sru      }
655114402Sru      if (dh < 0) {
656114402Sru	oh += dh;
657114402Sru	dh = -dh;
658114402Sru      }
659114402Sru      moveto(oh, ov);
660114402Sru      out1(put_rule);
661114402Sru      out4(-dv);
662114402Sru      out4(dh);
663114402Sru    }
664114402Sru  }
665114402Sru}
666114402Sru
667114402Sru// XXX Will this overflow?
668114402Sru
669114402Sruinline int milliinches(int n)
670114402Sru{
671114402Sru  return (n*1000 + font::res/2)/font::res;
672114402Sru}
673114402Sru
674114402Sruvoid draw_dvi_printer::set_line_thickness(const environment *env)
675114402Sru{
676114402Sru  int desired_pen_size
677114402Sru    = milliinches(line_thickness < 0
678114402Sru		  // Will this overflow?
679114402Sru		  ? env->size*RES_7227*linewidth/1000
680114402Sru		  : line_thickness);
681114402Sru  if (desired_pen_size != output_pen_size) {
682114402Sru    char buf[256];
683114402Sru    sprintf(buf, "pn %d", desired_pen_size);
684114402Sru    do_special(buf);
685114402Sru    output_pen_size = desired_pen_size;
686114402Sru  }
687114402Sru}
688114402Sru
689114402Sruvoid draw_dvi_printer::fill_next(const environment *env)
690114402Sru{
691114402Sru  unsigned int g;
692114402Sru  if (env->fill->is_default())
693114402Sru    g = 0;
694114402Sru  else {
695114402Sru    // currently, only BW support
696114402Sru    env->fill->get_gray(&g);
697114402Sru  }
698114402Sru  char buf[256];
699114402Sru  sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
700114402Sru  do_special(buf);
701114402Sru}
702114402Sru
703114402Sruvoid draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
704114402Sru{
705114402Sru  char buf[1024];
706114402Sru  int fill_flag = 0;
707114402Sru  switch (code) {
708114402Sru  case 'C':
709114402Sru    fill_flag = 1;
710114402Sru    // fall through
711114402Sru  case 'c':
712151497Sru    {
713151497Sru      // troff adds an extra argument to C
714151497Sru      if (np != 1 && !(code == 'C' && np == 2)) {
715151497Sru	error("1 argument required for circle");
716151497Sru	break;
717151497Sru      }
718151497Sru      moveto(env->hpos+p[0]/2, env->vpos);
719151497Sru      if (fill_flag)
720151497Sru	fill_next(env);
721151497Sru      else
722151497Sru	set_line_thickness(env);
723151497Sru      int rad;
724151497Sru      rad = milliinches(p[0]/2);
725151497Sru      sprintf(buf, "%s 0 0 %d %d 0 6.28319",
726151497Sru	      (fill_flag ? "ia" : "ar"),
727151497Sru	      rad,
728151497Sru	      rad);
729151497Sru      do_special(buf);
730114402Sru      break;
731114402Sru    }
732114402Sru  case 'l':
733114402Sru    if (np != 2) {
734114402Sru      error("2 arguments required for line");
735114402Sru      break;
736114402Sru    }
737114402Sru    moveto(env->hpos, env->vpos);
738114402Sru    set_line_thickness(env);
739114402Sru    do_special("pa 0 0");
740114402Sru    sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
741114402Sru    do_special(buf);
742114402Sru    do_special("fp");
743114402Sru    break;
744114402Sru  case 'E':
745114402Sru    fill_flag = 1;
746114402Sru    // fall through
747114402Sru  case 'e':
748114402Sru    if (np != 2) {
749114402Sru      error("2 arguments required for ellipse");
750114402Sru      break;
751114402Sru    }
752114402Sru    moveto(env->hpos+p[0]/2, env->vpos);
753114402Sru    if (fill_flag)
754114402Sru      fill_next(env);
755114402Sru    sprintf(buf, "%s 0 0 %d %d 0 6.28319",
756114402Sru	    (fill_flag ? "ia" : "ar"),
757114402Sru	    milliinches(p[0]/2),
758114402Sru	    milliinches(p[1]/2));
759114402Sru    do_special(buf);
760114402Sru    break;
761114402Sru  case 'P':
762114402Sru    fill_flag = 1;
763114402Sru    // fall through
764114402Sru  case 'p':
765114402Sru    {
766114402Sru      if (np & 1) {
767114402Sru	error("even number of arguments required for polygon");
768114402Sru	break;
769114402Sru      }
770114402Sru      if (np == 0) {
771114402Sru	error("no arguments for polygon");
772114402Sru	break;
773114402Sru      }
774114402Sru      moveto(env->hpos, env->vpos);
775114402Sru      if (fill_flag)
776114402Sru	fill_next(env);
777114402Sru      else
778114402Sru	set_line_thickness(env);
779114402Sru      do_special("pa 0 0");
780114402Sru      int h = 0, v = 0;
781114402Sru      for (int i = 0; i < np; i += 2) {
782114402Sru	h += p[i];
783114402Sru	v += p[i+1];
784114402Sru	sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
785114402Sru	do_special(buf);
786114402Sru      }
787114402Sru      do_special("pa 0 0");
788114402Sru      do_special(fill_flag ? "ip" : "fp");
789114402Sru      break;
790114402Sru    }
791114402Sru  case '~':
792114402Sru    {
793114402Sru      if (np & 1) {
794114402Sru	error("even number of arguments required for spline");
795114402Sru	break;
796114402Sru      }
797114402Sru      if (np == 0) {
798114402Sru	error("no arguments for spline");
799114402Sru	break;
800114402Sru      }
801114402Sru      moveto(env->hpos, env->vpos);
802114402Sru      set_line_thickness(env);
803114402Sru      do_special("pa 0 0");
804114402Sru      int h = 0, v = 0;
805114402Sru      for (int i = 0; i < np; i += 2) {
806114402Sru	h += p[i];
807114402Sru	v += p[i+1];
808114402Sru	sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
809114402Sru	do_special(buf);
810114402Sru      }
811114402Sru      do_special("sp");
812114402Sru      break;
813114402Sru    }
814114402Sru  case 'a':
815114402Sru    {
816114402Sru      if (np != 4) {
817114402Sru	error("4 arguments required for arc");
818114402Sru	break;
819114402Sru      }
820114402Sru      set_line_thickness(env);
821114402Sru      double c[2];
822114402Sru      if (adjust_arc_center(p, c)) {
823114402Sru	int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
824114402Sru	moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
825151497Sru	double start = atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]);
826151497Sru	double end = atan2(-c[1], -c[0]);
827151497Sru	if (end - start < 0)
828151497Sru	  start -= 2 * 3.14159265358;
829151497Sru	sprintf(buf, "ar 0 0 %d %d %f %f", rad, rad, start, end);
830114402Sru	do_special(buf);
831114402Sru      }
832114402Sru      else {
833114402Sru	moveto(env->hpos, env->vpos);
834114402Sru	do_special("pa 0 0");
835114402Sru	sprintf(buf,
836114402Sru		"pa %d %d",
837114402Sru		milliinches(p[0] + p[2]),
838114402Sru		milliinches(p[1] + p[3]));
839114402Sru	do_special(buf);
840114402Sru	do_special("fp");
841114402Sru      }
842114402Sru      break;
843114402Sru    }
844114402Sru  case 't':
845114402Sru    {
846114402Sru      if (np == 0) {
847114402Sru	line_thickness = -1;
848114402Sru      }
849114402Sru      else {
850114402Sru	// troff gratuitously adds an extra 0
851114402Sru	if (np != 1 && np != 2) {
852114402Sru	  error("0 or 1 argument required for thickness");
853114402Sru	  break;
854114402Sru	}
855114402Sru	line_thickness = p[0];
856114402Sru      }
857114402Sru      break;
858114402Sru    }
859114402Sru  case 'R':
860114402Sru    {
861114402Sru      if (np != 2) {
862114402Sru	error("2 arguments required for rule");
863114402Sru	break;
864114402Sru      }
865114402Sru      int dh = p[0];
866114402Sru      if (dh == 0)
867114402Sru	break;
868114402Sru      int dv = p[1];
869114402Sru      if (dv == 0)
870114402Sru	break;
871114402Sru      int oh = env->hpos;
872114402Sru      int ov = env->vpos;
873114402Sru      if (dv > 0) {
874114402Sru	ov += dv;
875114402Sru	dv = -dv;
876114402Sru      }
877114402Sru      if (dh < 0) {
878114402Sru	oh += dh;
879114402Sru	dh = -dh;
880114402Sru      }
881114402Sru      moveto(oh, ov);
882114402Sru      out1(put_rule);
883114402Sru      out4(-dv);
884114402Sru      out4(dh);
885114402Sru      break;
886114402Sru    }
887114402Sru  default:
888114402Sru    error("unrecognised drawing command `%1'", char(code));
889114402Sru    break;
890114402Sru  }
891114402Sru}
892114402Sru
893114402Srufont *dvi_printer::make_font(const char *nm)
894114402Sru{
895114402Sru  return dvi_font::load_dvi_font(nm);
896114402Sru}
897114402Sru
898114402Sruprinter *make_printer()
899114402Sru{
900114402Sru  if (draw_flag)
901114402Sru    return new draw_dvi_printer;
902114402Sru  else
903114402Sru    return new dvi_printer;
904114402Sru}
905114402Sru
906114402Srustatic void usage(FILE *stream);
907114402Sru
908114402Sruint main(int argc, char **argv)
909114402Sru{
910114402Sru  setlocale(LC_NUMERIC, "C");
911114402Sru  program_name = argv[0];
912114402Sru  static char stderr_buf[BUFSIZ];
913114402Sru  setbuf(stderr, stderr_buf);
914114402Sru  int c;
915114402Sru  static const struct option long_options[] = {
916114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
917114402Sru    { "version", no_argument, 0, 'v' },
918114402Sru    { NULL, 0, 0, 0 }
919114402Sru  };
920151497Sru  while ((c = getopt_long(argc, argv, "dF:I:lp:vw:", long_options, NULL))
921114402Sru	 != EOF)
922114402Sru    switch(c) {
923114402Sru    case 'd':
924114402Sru      draw_flag = 0;
925114402Sru      break;
926114402Sru    case 'l':
927114402Sru      landscape_flag = 1;
928114402Sru      break;
929114402Sru    case 'F':
930114402Sru      font::command_line_font_dir(optarg);
931114402Sru      break;
932151497Sru    case 'I':
933151497Sru      // ignore include search path
934151497Sru      break;
935114402Sru    case 'p':
936114402Sru      if (!font::scan_papersize(optarg, 0,
937114402Sru				&user_paper_length, &user_paper_width))
938114402Sru	error("invalid custom paper size `%1' ignored", optarg);
939114402Sru      break;
940114402Sru    case 'v':
941114402Sru      {
942114402Sru	printf("GNU grodvi (groff) version %s\n", Version_string);
943114402Sru	exit(0);
944114402Sru	break;
945114402Sru      }
946114402Sru    case 'w':
947114402Sru      if (sscanf(optarg, "%d", &linewidth) != 1
948114402Sru	  || linewidth < 0 || linewidth > 1000) {
949114402Sru	error("bad line width");
950114402Sru	linewidth = DEFAULT_LINEWIDTH;
951114402Sru      }
952114402Sru      break;
953114402Sru    case CHAR_MAX + 1: // --help
954114402Sru      usage(stdout);
955114402Sru      exit(0);
956114402Sru      break;
957114402Sru    case '?':
958114402Sru      usage(stderr);
959114402Sru      exit(1);
960114402Sru      break;
961114402Sru    default:
962114402Sru      assert(0);
963114402Sru    }
964114402Sru  SET_BINARY(fileno(stdout));
965114402Sru  if (optind >= argc)
966114402Sru    do_file("-");
967114402Sru  else {
968114402Sru    for (int i = optind; i < argc; i++)
969114402Sru      do_file(argv[i]);
970114402Sru  }
971114402Sru  return 0;
972114402Sru}
973114402Sru
974114402Srustatic void usage(FILE *stream)
975114402Sru{
976114402Sru  fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
977114402Sru	  program_name);
978114402Sru}
979