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