152284Sobrien/*	$NetBSD$	*/
2169689Skan
352284Sobrien// -*- C++ -*-
490075Sobrien/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
552284Sobrien   Free Software Foundation, Inc.
690075Sobrien     Written by James Clark (jjc@jclark.com)
790075Sobrien
890075SobrienThis file is part of groff.
990075Sobrien
1052284Sobriengroff is free software; you can redistribute it and/or modify it under
1190075Sobrienthe terms of the GNU General Public License as published by the Free
1290075SobrienSoftware Foundation; either version 2, or (at your option) any later
1390075Sobrienversion.
1490075Sobrien
1552284Sobriengroff is distributed in the hope that it will be useful, but WITHOUT ANY
1652284SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or
1790075SobrienFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18169689Skanfor more details.
19169689Skan
2052284SobrienYou should have received a copy of the GNU General Public License along
2152284Sobrienwith groff; see the file COPYING.  If not, write to the Free Software
2252284SobrienFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23132718Skan
24132718Skan#include "driver.h"
2552284Sobrien#include "nonposix.h"
2652284Sobrien#include "paper.h"
2790075Sobrien
28169689Skanextern "C" const char *Version_string;
2952284Sobrien
3052284Sobrien#define DEFAULT_LINEWIDTH 40
3152284Sobrienstatic int linewidth = DEFAULT_LINEWIDTH;
3252284Sobrien
3352284Sobrienstatic int draw_flag = 1;
3452284Sobrien
3552284Sobrienstatic int landscape_flag = 0;
36132718Skanstatic double user_paper_length = 0;
3752284Sobrienstatic double user_paper_width = 0;
3890075Sobrien
3952284Sobrien/* These values were chosen because:
4052284Sobrien
4152284Sobrien(MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
4252284Sobrien
4352284Sobrienand 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
4452284Sobrien
45132718SkanThe width in the groff font file is the product of MULTIPLIER and the
4652284Sobrienwidth in the tfm file. */
4752284Sobrien
4852284Sobrien#define RES 57816
4952284Sobrien#define RES_7227 (RES/7227)
5052284Sobrien#define UNITWIDTH 131072
5152284Sobrien#define SIZESCALE 100
52117395Skan#define MULTIPLIER 1
53117395Skan
54117395Skanclass dvi_font : public font {
55117395Skan  dvi_font(const char *);
56117395Skanpublic:
57132718Skan  int checksum;
58117395Skan  int design_size;
59117395Skan  ~dvi_font();
60117395Skan  void handle_unknown_font_command(const char *command, const char *arg,
61117395Skan				   const char *filename, int lineno);
62117395Skan  static dvi_font *load_dvi_font(const char *);
63117395Skan};
64117395Skan
65117395Skandvi_font *dvi_font::load_dvi_font(const char *s)
66117395Skan{
67117395Skan  dvi_font *f = new dvi_font(s);
68132718Skan  if (!f->load()) {
69117395Skan    delete f;
70117395Skan    return 0;
71117395Skan  }
72117395Skan  return f;
73117395Skan}
74117395Skan
75132718Skandvi_font::dvi_font(const char *nm)
76117395Skan: font(nm), checksum(0), design_size(0)
77117395Skan{
78117395Skan}
79117395Skan
80117395Skandvi_font::~dvi_font()
81117395Skan{
82117395Skan}
83117395Skan
84117395Skanvoid dvi_font::handle_unknown_font_command(const char *command,
85117395Skan					   const char *arg,
86117395Skan					   const char *filename, int lineno)
87117395Skan{
88117395Skan  char *ptr;
89117395Skan  if (strcmp(command, "checksum") == 0) {
90132718Skan    if (arg == 0)
91117395Skan      fatal_with_file_and_line(filename, lineno,
92117395Skan			       "`checksum' command requires an argument");
93117395Skan    checksum = int(strtol(arg, &ptr, 10));
94132718Skan    if (checksum == 0 && ptr == arg) {
95117395Skan      fatal_with_file_and_line(filename, lineno, "bad checksum");
96117395Skan    }
97117395Skan  }
98117395Skan  else if (strcmp(command, "designsize") == 0) {
99117395Skan    if (arg == 0)
100117395Skan      fatal_with_file_and_line(filename, lineno,
101117395Skan			       "`designsize' command requires an argument");
102117395Skan    design_size = int(strtol(arg, &ptr, 10));
103117395Skan    if (design_size == 0 && ptr == arg) {
104117395Skan      fatal_with_file_and_line(filename, lineno, "bad design size");
105117395Skan    }
106117395Skan  }
107169689Skan}
108169689Skan
109169689Skan#define FONTS_MAX 256
110169689Skan
111169689Skanstruct output_font {
112169689Skan  dvi_font *f;
113169689Skan  int point_size;
114169689Skan  output_font() : f(0) { }
115169689Skan};
116169689Skan
117169689Skanclass dvi_printer : public printer {
118169689Skan  FILE *fp;
119169689Skan  int max_drift;
120169689Skan  int byte_count;
121169689Skan  int last_bop;
122169689Skan  int page_count;
123169689Skan  int cur_h;
124169689Skan  int cur_v;
125169689Skan  int end_h;
126169689Skan  int max_h;
127169689Skan  int max_v;
128169689Skan  output_font output_font_table[FONTS_MAX];
129169689Skan  font *cur_font;
130169689Skan  int cur_point_size;
131169689Skan  color cur_color;
132169689Skan  int pushed;
13352284Sobrien  int pushed_h;
13452284Sobrien  int pushed_v;
13552284Sobrien  int have_pushed;
136132718Skan  void preamble();
13752284Sobrien  void postamble();
13890075Sobrien  void define_font(int);
13952284Sobrien  void set_font(int);
14052284Sobrien  void possibly_begin_line();
14152284Sobrien  void set_color(color *);
14252284Sobrienprotected:
14352284Sobrien  enum {
14452284Sobrien    id_byte = 2,
14552284Sobrien    set1 = 128,
14652284Sobrien    put1 = 133,
14752284Sobrien    put_rule = 137,
14852284Sobrien    bop = 139,
14952284Sobrien    eop = 140,
15052284Sobrien    push = 141,
15152284Sobrien    pop = 142,
15252284Sobrien    right1 = 143,
15352284Sobrien    down1 = 157,
15452284Sobrien    fnt_num_0 = 171,
15552284Sobrien    fnt1 = 235,
15652284Sobrien    xxx1 = 239,
15752284Sobrien    fnt_def1 = 243,
15852284Sobrien    pre = 247,
15952284Sobrien    post = 248,
160132718Skan    post_post = 249,
16152284Sobrien    filler = 223
16290075Sobrien  };
16352284Sobrien  int line_thickness;
16452284Sobrien
16590075Sobrien  void out1(int);
16652284Sobrien  void out2(int);
16752284Sobrien  void out3(int);
16852284Sobrien  void out4(int);
16952284Sobrien  void moveto(int, int);
17052284Sobrien  void out_string(const char *);
17152284Sobrien  void out_signed(unsigned char, int);
17252284Sobrien  void out_unsigned(unsigned char, int);
17352284Sobrien  void do_special(const char *);
17452284Sobrienpublic:
17552284Sobrien  dvi_printer();
17652284Sobrien  ~dvi_printer();
17752284Sobrien  font *make_font(const char *);
178132718Skan  void begin_page(int);
17952284Sobrien  void end_page(int);
18090075Sobrien  void set_char(int, font *, const environment *, int w, const char *name);
18152284Sobrien  void special(char *arg, const environment *env, char type);
18252284Sobrien  void end_of_line();
18390075Sobrien  void draw(int code, int *p, int np, const environment *env);
18490075Sobrien};
185132718Skan
18690075Sobrien
18790075Sobrienclass draw_dvi_printer : public dvi_printer {
18890075Sobrien  int output_pen_size;
189117395Skan  void set_line_thickness(const environment *);
19052284Sobrien  void fill_next(const environment *);
19152284Sobrienpublic:
19252284Sobrien  draw_dvi_printer();
193132718Skan  ~draw_dvi_printer();
19452284Sobrien  void draw(int code, int *p, int np, const environment *env);
195132718Skan  void end_page(int);
19652284Sobrien};
19752284Sobrien
19890075Sobriendvi_printer::dvi_printer()
19952284Sobrien: fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
20052284Sobrien  cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
201132718Skan{
20252284Sobrien  if (font::res != RES)
20390075Sobrien    fatal("resolution must be %1", RES);
20490075Sobrien  if (font::unitwidth != UNITWIDTH)
205132718Skan    fatal("unitwidth must be %1", UNITWIDTH);
20690075Sobrien  if (font::hor != 1)
20790075Sobrien    fatal("hor must be equal to 1");
20890075Sobrien  if (font::vert != 1)
20990075Sobrien    fatal("vert must be equal to 1");
21090075Sobrien  if (font::sizescale != SIZESCALE)
21152284Sobrien    fatal("sizescale must be equal to %1", SIZESCALE);
21252284Sobrien  max_drift = font::res/1000;	// this is fairly arbitrary
21352284Sobrien  preamble();
21452284Sobrien}
21552284Sobrien
216132718Skandvi_printer::~dvi_printer()
21752284Sobrien{
21890075Sobrien  postamble();
21952284Sobrien}
22052284Sobrien
22152284Sobrien
22252284Sobriendraw_dvi_printer::draw_dvi_printer()
22352284Sobrien: output_pen_size(-1)
22490075Sobrien{
22552284Sobrien}
22652284Sobrien
227132718Skandraw_dvi_printer::~draw_dvi_printer()
22852284Sobrien{
22990075Sobrien}
23052284Sobrien
23152284Sobrien
23252284Sobrienvoid dvi_printer::out1(int n)
23352284Sobrien{
23452284Sobrien  byte_count += 1;
23552284Sobrien  putc(n & 0xff, fp);
23652284Sobrien}
237117395Skan
23852284Sobrienvoid dvi_printer::out2(int n)
239117395Skan{
240132718Skan  byte_count += 2;
24152284Sobrien  putc((n >> 8) & 0xff, fp);
242117395Skan  putc(n & 0xff, fp);
243117395Skan}
244117395Skan
245117395Skanvoid dvi_printer::out3(int n)
246117395Skan{
247117395Skan  byte_count += 3;
24852284Sobrien  putc((n >> 16) & 0xff, fp);
249117395Skan  putc((n >> 8) & 0xff, fp);
25052284Sobrien  putc(n & 0xff, fp);
25190075Sobrien}
252117395Skan
253117395Skanvoid dvi_printer::out4(int n)
25452284Sobrien{
25590075Sobrien  byte_count += 4;
256117395Skan  putc((n >> 24) & 0xff, fp);
25752284Sobrien  putc((n >> 16) & 0xff, fp);
25852284Sobrien  putc((n >> 8) & 0xff, fp);
259117395Skan  putc(n & 0xff, fp);
260132718Skan}
261117395Skan
262117395Skanvoid dvi_printer::out_string(const char *s)
263117395Skan{
264117395Skan  out1(strlen(s));
265117395Skan  while (*s != 0)
266117395Skan    out1(*s++);
267117395Skan}
268117395Skan
269117395Skan
270117395Skanvoid dvi_printer::end_of_line()
271117395Skan{
27252284Sobrien  if (pushed) {
27352284Sobrien    out1(pop);
27452284Sobrien    pushed = 0;
275132718Skan    cur_h = pushed_h;
27652284Sobrien    cur_v = pushed_v;
277117395Skan  }
278117395Skan}
279117395Skan
280132718Skanvoid dvi_printer::possibly_begin_line()
28152284Sobrien{
282117395Skan  if (!pushed) {
283117395Skan    have_pushed = pushed = 1;
284132718Skan    pushed_h = cur_h;
285132718Skan    pushed_v = cur_v;
286132718Skan    out1(push);
287132718Skan  }
288132718Skan}
289132718Skan
29052284Sobrienint scale(int x, int z)
29152284Sobrien{
29252284Sobrien  int sw;
29390075Sobrien  int a, b, c, d;
29452284Sobrien  int alpha, beta;
29552284Sobrien  alpha = 16*z; beta = 16;
296132718Skan  while (z >= 040000000L) {
29752284Sobrien    z /= 2; beta /= 2;
298117395Skan  }
299117395Skan  d = x & 255;
300117395Skan  c = (x >> 8) & 255;
301117395Skan  b = (x >> 16) & 255;
302117395Skan  a = (x >> 24) & 255;
303132718Skan  sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
304117395Skan  if (a == 255)
305169689Skan    sw -= alpha;
306117395Skan  else
307117395Skan    assert(a == 0);
308117395Skan  return sw;
309117395Skan}
310117395Skan
31152284Sobrienvoid dvi_printer::set_color(color *col)
312117395Skan{
313117395Skan  cur_color = *col;
314117395Skan  char buf[256];
315117395Skan  unsigned int components[4];
316117395Skan  color_scheme cs = col->get_components(components);
31752284Sobrien  switch (cs) {
31852284Sobrien  case DEFAULT:
319169689Skan    sprintf(buf, "color gray 0");
320169689Skan    break;
321169689Skan  case RGB:
322169689Skan    sprintf(buf, "color rgb %.3g %.3g %.3g",
323169689Skan		 double(Red) / color::MAX_COLOR_VAL,
324169689Skan		 double(Green) / color::MAX_COLOR_VAL,
325169689Skan		 double(Blue) / color::MAX_COLOR_VAL);
326169689Skan    break;
327169689Skan  case CMY:
328169689Skan    col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
329169689Skan    // fall through
330169689Skan  case CMYK:
331169689Skan    sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
332169689Skan		 double(Cyan) / color::MAX_COLOR_VAL,
333169689Skan		 double(Magenta) / color::MAX_COLOR_VAL,
334169689Skan		 double(Yellow) / color::MAX_COLOR_VAL,
335169689Skan		 double(Black) / color::MAX_COLOR_VAL);
336169689Skan    break;
33790075Sobrien  case GRAY:
338117395Skan    sprintf(buf, "color gray %.3g",
33952284Sobrien		 double(Gray) / color::MAX_COLOR_VAL);
340117395Skan    break;
341132718Skan  }
34252284Sobrien  do_special(buf);
343117395Skan}
344117395Skan
345117395Skanvoid dvi_printer::set_char(int idx, font *f, const environment *env,
346117395Skan			   int w, const char *)
347117395Skan{
34852284Sobrien  if (*env->col != cur_color)
349117395Skan    set_color(env->col);
35052284Sobrien  int code = f->get_code(idx);
35190075Sobrien  if (env->size != cur_point_size || f != cur_font) {
352132718Skan    cur_font = f;
353117395Skan    cur_point_size = env->size;
35452284Sobrien    int i;
35590075Sobrien    for (i = 0;; i++) {
356117395Skan      if (i >= FONTS_MAX) {
35752284Sobrien	fatal("too many output fonts required");
35890075Sobrien      }
359117395Skan      if (output_font_table[i].f == 0) {
360132718Skan	output_font_table[i].f = (dvi_font *)cur_font;
361117395Skan	output_font_table[i].point_size = cur_point_size;
362117395Skan	define_font(i);
363117395Skan      }
364117395Skan      if (output_font_table[i].f == cur_font
365117395Skan	  && output_font_table[i].point_size == cur_point_size)
366117395Skan	break;
367117395Skan    }
368117395Skan    set_font(i);
369117395Skan  }
370117395Skan  int distance = env->hpos - cur_h;
37190075Sobrien  if (env->hpos != end_h && distance != 0) {
372117395Skan    out_signed(right1, distance);
37390075Sobrien    cur_h = env->hpos;
374117395Skan  }
375132718Skan  else if (distance > max_drift) {
37690075Sobrien    out_signed(right1, distance - max_drift);
377117395Skan    cur_h = env->hpos - max_drift;
378117395Skan  }
379117395Skan  else if (distance < -max_drift) {
380117395Skan    out_signed(right1, distance + max_drift);
381117395Skan    cur_h = env->hpos + max_drift;
382117395Skan  }
383117395Skan  if (env->vpos != cur_v) {
38490075Sobrien    out_signed(down1, env->vpos - cur_v);
38590075Sobrien    cur_v = env->vpos;
386132718Skan  }
387117395Skan  possibly_begin_line();
38890075Sobrien  end_h = env->hpos + w;
389117395Skan  cur_h += scale(f->get_width(idx, UNITWIDTH)/MULTIPLIER,
390117395Skan		 cur_point_size*RES_7227);
39190075Sobrien  if (cur_h > max_h)
39290075Sobrien    max_h = cur_h;
393117395Skan  if (cur_v > max_v)
394132718Skan    max_v = cur_v;
395117395Skan  if (code >= 0 && code <= 127)
396117395Skan    out1(code);
397117395Skan  else
398117395Skan    out_unsigned(set1, code);
399117395Skan}
400117395Skan
401117395Skanvoid dvi_printer::define_font(int i)
402117395Skan{
403117395Skan  out_unsigned(fnt_def1, i);
404117395Skan  dvi_font *f = output_font_table[i].f;
40552284Sobrien  out4(f->checksum);
406117395Skan  out4(output_font_table[i].point_size*RES_7227);
40752284Sobrien  out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
408117395Skan  const char *nm = f->get_internal_name();
409132718Skan  out1(0);
41052284Sobrien  out_string(nm);
411117395Skan}
412117395Skan
413117395Skanvoid dvi_printer::set_font(int i)
414117395Skan{
415117395Skan  if (i >= 0 && i <= 63)
41652284Sobrien    out1(fnt_num_0 + i);
417117395Skan  else
41852284Sobrien    out_unsigned(fnt1, i);
41990075Sobrien}
420132718Skan
421117395Skanvoid dvi_printer::out_signed(unsigned char base, int param)
42252284Sobrien{
42390075Sobrien  if (-128 <= param && param < 128) {
424117395Skan    out1(base);
42552284Sobrien    out1(param);
42652284Sobrien  }
427117395Skan  else if (-32768 <= param && param < 32768) {
428132718Skan    out1(base+1);
429117395Skan    out2(param);
430117395Skan  }
431117395Skan  else if (-(1 << 23) <= param && param < (1 << 23)) {
432117395Skan    out1(base+2);
433117395Skan    out3(param);
43490075Sobrien  }
435117395Skan  else {
436117395Skan    out1(base+3);
437117395Skan    out4(param);
438117395Skan  }
439117395Skan}
440117395Skan
441117395Skanvoid dvi_printer::out_unsigned(unsigned char base, int param)
442132718Skan{
44390075Sobrien  if (param >= 0) {
444117395Skan    if (param < 256) {
44590075Sobrien      out1(base);
44690075Sobrien      out1(param);
447117395Skan    }
44890075Sobrien    else if (param < 65536) {
449117395Skan      out1(base+1);
45090075Sobrien      out2(param);
451117395Skan    }
45290075Sobrien    else if (param < (1 << 24)) {
45390075Sobrien      out1(base+2);
45452284Sobrien      out3(param);
455117395Skan    }
45652284Sobrien    else {
457117395Skan      out1(base+3);
458132718Skan      out4(param);
45952284Sobrien    }
460117395Skan  }
461117395Skan  else {
462117395Skan    out1(base+3);
463117395Skan    out4(param);
464117395Skan  }
465117395Skan}
46652284Sobrien
467117395Skanvoid dvi_printer::preamble()
46852284Sobrien{
46990075Sobrien  out1(pre);
470117395Skan  out1(id_byte);
471117395Skan  out4(254000);
47252284Sobrien  out4(font::res);
47390075Sobrien  out4(1000);
474117395Skan  out1(0);
47552284Sobrien}
47652284Sobrien
477117395Skanvoid dvi_printer::postamble()
478132718Skan{
479117395Skan  int tem = byte_count;
480117395Skan  out1(post);
481117395Skan  out4(last_bop);
482117395Skan  out4(254000);
483117395Skan  out4(font::res);
484117395Skan  out4(1000);
485117395Skan  out4(max_v);
486117395Skan  out4(max_h);
487117395Skan  out2(have_pushed); // stack depth
488117395Skan  out2(page_count);
489117395Skan  int i;
49090075Sobrien  for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
491117395Skan    define_font(i);
49252284Sobrien  out1(post_post);
493117395Skan  out4(tem);
494132718Skan  out1(id_byte);
49552284Sobrien  for (i = 0; i < 4 || byte_count % 4 != 0; i++)
496117395Skan    out1(filler);
497117395Skan}
498117395Skan
499117395Skanvoid dvi_printer::begin_page(int i)
500117395Skan{
501117395Skan  page_count++;
50252284Sobrien  int tem = byte_count;
503117395Skan  out1(bop);
50452284Sobrien  out4(i);
50590075Sobrien  for (int j = 1; j < 10; j++)
506117395Skan    out4(0);
507117395Skan  out4(last_bop);
50852284Sobrien  last_bop = tem;
50990075Sobrien  // By convention position (0,0) in a dvi file is placed at (1in, 1in).
510117395Skan  cur_h = font::res;
51152284Sobrien  cur_v = font::res;
51252284Sobrien  end_h = 0;
513117395Skan  if (page_count == 1) {
514132718Skan    char buf[256];
515117395Skan    // at least dvips uses this
516117395Skan    double length = user_paper_length ? user_paper_length :
517117395Skan					double(font::paperlength) / font::res;
518117395Skan    double width = user_paper_width ? user_paper_width :
519117395Skan				      double(font::paperwidth) / font::res;
520117395Skan    if (width > 0 && length > 0) {
521117395Skan      sprintf(buf, "papersize=%.3fin,%.3fin",
522117395Skan	      landscape_flag ? length : width,
523117395Skan	      landscape_flag ? width : length);
524117395Skan      do_special(buf);
525117395Skan    }
52690075Sobrien  }
52790075Sobrien  if (cur_color != default_color)
52890075Sobrien    set_color(&cur_color);
52952284Sobrien}
53052284Sobrien
531132718Skanvoid dvi_printer::end_page(int)
53252284Sobrien{
53390075Sobrien  set_color(&default_color);
53490075Sobrien  if (pushed)
53590075Sobrien    end_of_line();
536169689Skan  out1(eop);
53752284Sobrien  cur_font = 0;
538169689Skan}
53990075Sobrien
540169689Skanvoid draw_dvi_printer::end_page(int len)
54190075Sobrien{
542117395Skan  dvi_printer::end_page(len);
543169689Skan  output_pen_size = -1;
54490075Sobrien}
54590075Sobrien
54652284Sobrienvoid dvi_printer::do_special(const char *s)
54752284Sobrien{
54890075Sobrien  int len = strlen(s);
54990075Sobrien  if (len == 0)
55090075Sobrien    return;
551169689Skan  possibly_begin_line();
55290075Sobrien  out_unsigned(xxx1, len);
55390075Sobrien  while (*s)
55490075Sobrien    out1(*s++);
55552284Sobrien}
556169689Skan
55790075Sobrienvoid dvi_printer::special(char *arg, const environment *env, char type)
55890075Sobrien{
55990075Sobrien  if (type != 'p')
56090075Sobrien    return;
56190075Sobrien  moveto(env->hpos, env->vpos);
56290075Sobrien  do_special(arg);
56390075Sobrien}
56490075Sobrien
56590075Sobrienvoid dvi_printer::moveto(int h, int v)
56690075Sobrien{
56790075Sobrien  if (h != cur_h) {
56890075Sobrien    out_signed(right1, h - cur_h);
56990075Sobrien    cur_h = h;
57090075Sobrien    if (cur_h > max_h)
571132718Skan      max_h = cur_h;
57290075Sobrien  }
57390075Sobrien  if (v != cur_v) {
57490075Sobrien    out_signed(down1, v - cur_v);
57590075Sobrien    cur_v = v;
576169689Skan    if (cur_v > max_v)
57790075Sobrien      max_v = cur_v;
578169689Skan  }
57952284Sobrien  end_h = 0;
580169689Skan}
58190075Sobrien
582117395Skanvoid dvi_printer::draw(int code, int *p, int np, const environment *env)
58390075Sobrien{
58490075Sobrien  if (code == 'l') {
58552284Sobrien    int x = 0, y = 0;
58652284Sobrien    int height = 0, width = 0;
58752284Sobrien    int thickness;
58890075Sobrien    if (line_thickness < 0)
58990075Sobrien      thickness = env->size*RES_7227*linewidth/1000;
59090075Sobrien    else if (line_thickness > 0)
591169689Skan      thickness = line_thickness;
59290075Sobrien    else
59390075Sobrien      thickness = 1;
59490075Sobrien    if (np != 2) {
59552284Sobrien      error("2 arguments required for line");
596169689Skan    }
59790075Sobrien    else if (p[0] == 0) {
59890075Sobrien      // vertical rule
59990075Sobrien      if (p[1] > 0) {
60090075Sobrien	x = env->hpos - thickness/2;
60190075Sobrien	y = env->vpos + p[1] + thickness/2;
60290075Sobrien	height = p[1] + thickness;
60390075Sobrien	width = thickness;
60490075Sobrien      }
60590075Sobrien      else if (p[1] < 0) {
60690075Sobrien	x = env->hpos - thickness/2;
60790075Sobrien	y = env->vpos + thickness/2;
60890075Sobrien	height = thickness - p[1];
60990075Sobrien	width = thickness;
61090075Sobrien      }
611132718Skan    }
61290075Sobrien    else if (p[1] == 0) {
61390075Sobrien      if (p[0] > 0) {
61490075Sobrien	x = env->hpos - thickness/2;
61590075Sobrien	y = env->vpos + thickness/2;
616169689Skan	height = thickness;
61790075Sobrien	width = p[0] + thickness;
618169689Skan      }
61952284Sobrien      else if (p[0] < 0) {
620169689Skan	x = env->hpos - p[0] - thickness/2;
62190075Sobrien	y = env->vpos + thickness/2;
622117395Skan	height = thickness;
62352284Sobrien	width = thickness - p[0];
62490075Sobrien      }
62590075Sobrien    }
62690075Sobrien    if (height != 0) {
62752284Sobrien      moveto(x, y);
628169689Skan      out1(put_rule);
62990075Sobrien      out4(height);
63090075Sobrien      out4(width);
631169689Skan    }
63290075Sobrien  }
63390075Sobrien  else if (code == 't') {
63490075Sobrien    if (np == 0) {
63552284Sobrien      line_thickness = -1;
636169689Skan    }
63790075Sobrien    else {
63890075Sobrien      // troff gratuitously adds an extra 0
63990075Sobrien      if (np != 1 && np != 2)
64090075Sobrien	error("0 or 1 argument required for thickness");
64190075Sobrien      else
64290075Sobrien	line_thickness = p[0];
64390075Sobrien    }
64490075Sobrien  }
64552284Sobrien  else if (code == 'R') {
64652284Sobrien    if (np != 2)
64790075Sobrien      error("2 arguments required for rule");
64890075Sobrien    else if (p[0] != 0 || p[1] != 0) {
64952284Sobrien      int dh = p[0];
65052284Sobrien      int dv = p[1];
651132718Skan      int oh = env->hpos;
65252284Sobrien      int ov = env->vpos;
65390075Sobrien      if (dv > 0) {
65490075Sobrien	ov += dv;
65590075Sobrien	dv = -dv;
656169689Skan      }
65752284Sobrien      if (dh < 0) {
658169689Skan	oh += dh;
65952284Sobrien	dh = -dh;
660169689Skan      }
66190075Sobrien      moveto(oh, ov);
662117395Skan      out1(put_rule);
66352284Sobrien      out4(-dv);
66490075Sobrien      out4(dh);
66552284Sobrien    }
66652284Sobrien  }
66752284Sobrien}
668169689Skan
66990075Sobrien// XXX Will this overflow?
67090075Sobrien
671169689Skaninline int milliinches(int n)
67290075Sobrien{
67390075Sobrien  return (n*1000 + font::res/2)/font::res;
67490075Sobrien}
67552284Sobrien
676169689Skanvoid draw_dvi_printer::set_line_thickness(const environment *env)
67790075Sobrien{
67890075Sobrien  int desired_pen_size
679117395Skan    = milliinches(line_thickness < 0
68090075Sobrien		  // Will this overflow?
68190075Sobrien		  ? env->size*RES_7227*linewidth/1000
68290075Sobrien		  : line_thickness);
68390075Sobrien  if (desired_pen_size != output_pen_size) {
68490075Sobrien    char buf[256];
68590075Sobrien    sprintf(buf, "pn %d", desired_pen_size);
68690075Sobrien    do_special(buf);
68790075Sobrien    output_pen_size = desired_pen_size;
68890075Sobrien  }
68990075Sobrien}
69090075Sobrien
691132718Skanvoid draw_dvi_printer::fill_next(const environment *env)
69290075Sobrien{
693169689Skan  unsigned int g;
694169689Skan  if (env->fill->is_default())
69590075Sobrien    g = 0;
696169689Skan  else {
697169689Skan    // currently, only BW support
69890075Sobrien    env->fill->get_gray(&g);
69990075Sobrien  }
70090075Sobrien  char buf[256];
70190075Sobrien  sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
70290075Sobrien  do_special(buf);
70390075Sobrien}
704132718Skan
70590075Sobrienvoid draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
70690075Sobrien{
70790075Sobrien  char buf[1024];
70890075Sobrien  int fill_flag = 0;
70990075Sobrien  switch (code) {
71052284Sobrien  case 'C':
71190075Sobrien    fill_flag = 1;
71252284Sobrien    // fall through
71390075Sobrien  case 'c':
71490075Sobrien    {
71590075Sobrien      // troff adds an extra argument to C
71690075Sobrien      if (np != 1 && !(code == 'C' && np == 2)) {
71790075Sobrien	error("1 argument required for circle");
71852284Sobrien	break;
71990075Sobrien      }
72090075Sobrien      moveto(env->hpos+p[0]/2, env->vpos);
72190075Sobrien      if (fill_flag)
72290075Sobrien	fill_next(env);
72352284Sobrien      else
72490075Sobrien	set_line_thickness(env);
72590075Sobrien      int rad;
72690075Sobrien      rad = milliinches(p[0]/2);
72790075Sobrien      sprintf(buf, "%s 0 0 %d %d 0 6.28319",
72852284Sobrien	      (fill_flag ? "ia" : "ar"),
72990075Sobrien	      rad,
73090075Sobrien	      rad);
73152284Sobrien      do_special(buf);
73252284Sobrien      break;
73352284Sobrien    }
734132718Skan  case 'l':
73552284Sobrien    if (np != 2) {
73690075Sobrien      error("2 arguments required for line");
73790075Sobrien      break;
73890075Sobrien    }
73952284Sobrien    moveto(env->hpos, env->vpos);
74052284Sobrien    set_line_thickness(env);
74152284Sobrien    do_special("pa 0 0");
74290075Sobrien    sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
74390075Sobrien    do_special(buf);
74490075Sobrien    do_special("fp");
74590075Sobrien    break;
74690075Sobrien  case 'E':
74790075Sobrien    fill_flag = 1;
74890075Sobrien    // fall through
74990075Sobrien  case 'e':
75090075Sobrien    if (np != 2) {
75152284Sobrien      error("2 arguments required for ellipse");
75252284Sobrien      break;
75352284Sobrien    }
75452284Sobrien    moveto(env->hpos+p[0]/2, env->vpos);
755132718Skan    if (fill_flag)
75690075Sobrien      fill_next(env);
75790075Sobrien    sprintf(buf, "%s 0 0 %d %d 0 6.28319",
75890075Sobrien	    (fill_flag ? "ia" : "ar"),
759117395Skan	    milliinches(p[0]/2),
76090075Sobrien	    milliinches(p[1]/2));
76190075Sobrien    do_special(buf);
76290075Sobrien    break;
76390075Sobrien  case 'P':
76490075Sobrien    fill_flag = 1;
76590075Sobrien    // fall through
766117395Skan  case 'p':
76790075Sobrien    {
76890075Sobrien      if (np & 1) {
76990075Sobrien	error("even number of arguments required for polygon");
770117395Skan	break;
771117395Skan      }
77290075Sobrien      if (np == 0) {
77390075Sobrien	error("no arguments for polygon");
774117395Skan	break;
77590075Sobrien      }
77690075Sobrien      moveto(env->hpos, env->vpos);
77790075Sobrien      if (fill_flag)
778132718Skan	fill_next(env);
779117395Skan      else
780117395Skan	set_line_thickness(env);
781117395Skan      do_special("pa 0 0");
782117395Skan      int h = 0, v = 0;
783117395Skan      for (int i = 0; i < np; i += 2) {
784132718Skan	h += p[i];
785132718Skan	v += p[i+1];
78652284Sobrien	sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
78752284Sobrien	do_special(buf);
78852284Sobrien      }
78952284Sobrien      do_special("pa 0 0");
79052284Sobrien      do_special(fill_flag ? "ip" : "fp");
79152284Sobrien      break;
79252284Sobrien    }
79352284Sobrien  case '~':
79452284Sobrien    {
79590075Sobrien      if (np & 1) {
79652284Sobrien	error("even number of arguments required for spline");
79752284Sobrien	break;
798      }
799      if (np == 0) {
800	error("no arguments for spline");
801	break;
802      }
803      moveto(env->hpos, env->vpos);
804      set_line_thickness(env);
805      do_special("pa 0 0");
806      int h = 0, v = 0;
807      for (int i = 0; i < np; i += 2) {
808	h += p[i];
809	v += p[i+1];
810	sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
811	do_special(buf);
812      }
813      do_special("sp");
814      break;
815    }
816  case 'a':
817    {
818      if (np != 4) {
819	error("4 arguments required for arc");
820	break;
821      }
822      set_line_thickness(env);
823      double c[2];
824      if (adjust_arc_center(p, c)) {
825	int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
826	moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
827	double start = atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]);
828	double end = atan2(-c[1], -c[0]);
829	if (end - start < 0)
830	  start -= 2 * 3.14159265358;
831	sprintf(buf, "ar 0 0 %d %d %f %f", rad, rad, start, end);
832	do_special(buf);
833      }
834      else {
835	moveto(env->hpos, env->vpos);
836	do_special("pa 0 0");
837	sprintf(buf,
838		"pa %d %d",
839		milliinches(p[0] + p[2]),
840		milliinches(p[1] + p[3]));
841	do_special(buf);
842	do_special("fp");
843      }
844      break;
845    }
846  case 't':
847    {
848      if (np == 0) {
849	line_thickness = -1;
850      }
851      else {
852	// troff gratuitously adds an extra 0
853	if (np != 1 && np != 2) {
854	  error("0 or 1 argument required for thickness");
855	  break;
856	}
857	line_thickness = p[0];
858      }
859      break;
860    }
861  case 'R':
862    {
863      if (np != 2) {
864	error("2 arguments required for rule");
865	break;
866      }
867      int dh = p[0];
868      if (dh == 0)
869	break;
870      int dv = p[1];
871      if (dv == 0)
872	break;
873      int oh = env->hpos;
874      int ov = env->vpos;
875      if (dv > 0) {
876	ov += dv;
877	dv = -dv;
878      }
879      if (dh < 0) {
880	oh += dh;
881	dh = -dh;
882      }
883      moveto(oh, ov);
884      out1(put_rule);
885      out4(-dv);
886      out4(dh);
887      break;
888    }
889  default:
890    error("unrecognised drawing command `%1'", char(code));
891    break;
892  }
893}
894
895font *dvi_printer::make_font(const char *nm)
896{
897  return dvi_font::load_dvi_font(nm);
898}
899
900printer *make_printer()
901{
902  if (draw_flag)
903    return new draw_dvi_printer;
904  else
905    return new dvi_printer;
906}
907
908static void usage(FILE *stream);
909
910int main(int argc, char **argv)
911{
912  setlocale(LC_NUMERIC, "C");
913  program_name = argv[0];
914  static char stderr_buf[BUFSIZ];
915  setbuf(stderr, stderr_buf);
916  int c;
917  static const struct option long_options[] = {
918    { "help", no_argument, 0, CHAR_MAX + 1 },
919    { "version", no_argument, 0, 'v' },
920    { NULL, 0, 0, 0 }
921  };
922  while ((c = getopt_long(argc, argv, "dF:I:lp:vw:", long_options, NULL))
923	 != EOF)
924    switch(c) {
925    case 'd':
926      draw_flag = 0;
927      break;
928    case 'l':
929      landscape_flag = 1;
930      break;
931    case 'F':
932      font::command_line_font_dir(optarg);
933      break;
934    case 'I':
935      // ignore include search path
936      break;
937    case 'p':
938      if (!font::scan_papersize(optarg, 0,
939				&user_paper_length, &user_paper_width))
940	error("invalid custom paper size `%1' ignored", optarg);
941      break;
942    case 'v':
943      {
944	printf("GNU grodvi (groff) version %s\n", Version_string);
945	exit(0);
946	break;
947      }
948    case 'w':
949      if (sscanf(optarg, "%d", &linewidth) != 1
950	  || linewidth < 0 || linewidth > 1000) {
951	error("bad line width");
952	linewidth = DEFAULT_LINEWIDTH;
953      }
954      break;
955    case CHAR_MAX + 1: // --help
956      usage(stdout);
957      exit(0);
958      break;
959    case '?':
960      usage(stderr);
961      exit(1);
962      break;
963    default:
964      assert(0);
965    }
966  SET_BINARY(fileno(stdout));
967  if (optind >= argc)
968    do_file("-");
969  else {
970    for (int i = optind; i < argc; i++)
971      do_file(argv[i]);
972  }
973  return 0;
974}
975
976static void usage(FILE *stream)
977{
978  fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
979	  program_name);
980}
981